Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
289 views
in Technique[技术] by (71.8m points)

ios - Trouble creating xmpp muc room: Code 503 (service unavailable)

My code to create a room:

XMPPRoomCoreDataStorage *rosterstorage = [[XMPPRoomCoreDataStorage alloc] init];
XMPPRoom *xmppRoom = [[XMPPRoom alloc] initWithRoomStorage:rosterstorage jid:[XMPPJID jidWithString:@"groupchat@xmpp.getkismet.com/groupchat"] dispatchQueue:dispatch_get_main_queue()];

[xmppRoom activate:[[self appDelegate] xmppStream]];
if ([xmppRoom preJoinWithNickname:@"nameToCreateRoom"]) 
{
    NSLog(@"room created");
    [xmppRoom joinRoomUsingNickname:self.userName history:nil];
}
[xmppRoom fetchConfigurationForm];
[xmppRoom configureRoomUsingOptions:nil];
[xmppRoom addDelegate:[self appDelegate] delegateQueue:dispatch_get_main_queue()];

Debug:

2012-08-03 07:46:29.204 iPhoneXMPP[9887:fb03] room created
2012-08-03 07:46:29:230 iPhoneXMPP[9887:15003] SEND: <iq type="get" to="groupchat@xmpp.getkismet.com" id="B793062B-0E09-492F-BC0F-703503AAA664"><query xmlns="http://jabber.org/protocol/muc#owner"/></iq>
2012-08-03 07:46:29:237 iPhoneXMPP[9887:15003] SEND: <iq type="set" to="groupchat@xmpp.getkismet.com" id="392D5BFC-707B-4F68-A829-56F949F4E96D"><query xmlns="http://jabber.org/protocol/muc#owner"><x xmlns="jabber:x:data" type="submit"/></query></iq>
2012-08-03 07:46:29:326 iPhoneXMPP[9887:14f03] SEND: <presence to="groupchat@xmpp.getkismet.com"><x xmlns="http://jabber.org/protocol/muc"/><x xmlns="vcard-temp:x:update"><photo>91217a961321f8f6380ea2feefd0632353ad296c</photo></x><c xmlns="http://jabber.org/protocol/caps" hash="sha-1" node="http://code.google.com/p/xmppframework" ver="VyOFcFX6+YNmKssVXSBKGFP0BS4="/></presence>
2012-08-03 07:46:29:327 iPhoneXMPP[9887:14f03] RECV: <iq xmlns="jabber:client" from="groupchat@xmpp.getkismet.com" to="lee@xmpp.getkismet.com/41068195801343976386548353" type="error" id="B793062B-0E09-492F-BC0F-703503AAA664"><query xmlns="http://jabber.org/protocol/muc#owner"/><error code="503" type="cancel"><service-unavailable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></iq>
2012-08-03 07:46:29:343 iPhoneXMPP[9887:fb03] iPhoneXMPPAppDelegate: xmppStream:didReceiveIQ:
2012-08-03 07:46:29:421 iPhoneXMPP[9887:15003] RECV: <iq xmlns="jabber:client" from="groupchat@xmpp.getkismet.com" to="lee@xmpp.getkismet.com/41068195801343976386548353" type="error" id="392D5BFC-707B-4F68-A829-56F949F4E96D"><query xmlns="http://jabber.org/protocol/muc#owner"><x xmlns="jabber:x:data" type="submit"/></query><error code="503" type="cancel"><service-unavailable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></iq>
2012-08-03 07:46:29:440 iPhoneXMPP[9887:fb03] iPhoneXMPPAppDelegate: xmppStream:didReceiveIQ:

I see that it is creating/joining groupchat@xmpp.getkismet.com and not groupchat@xmpp.getkismet.com/groupchat like I specified. I read that this is most likely the problem. However, I have specified for the full jid, so I'm lost.

Thanks in advance to all who help.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

First, take look here XEP-0045: Multi-User Chat.
As you can see, first you have to discover which capabilities your user (XMPPJID) has on the Jabber server.

To do this, send next command to your Jabber Server:

<iq from='user@jabber.server.com/resource' id='some_expression' to='jabber.server.com' type='get'>
    <query xmlns='http://jabber.org/protocol/disco#items'/>
</iq>

or writen in objective-c using XMPP library functions:

NSError *error = nil;
NSXMLElement *query = [[NSXMLElement alloc] initWithXMLString:@"<query xmlns='http://jabber.org/protocol/disco#items'/>" 
                                                        error:&error];
XMPPIQ *iq = [XMPPIQ iqWithType:@"get" 
                             to:[XMPPJID jidWithString:@"jabber.server.com"] 
                      elementID:[xmppStream generateUUID] child:query];
[xmppStream sendElement:iq];

Now listen response from server in XMPPStream delegate - (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq and server response should be something like this:

<iq from='jabber.server.com' id='some_expression' to='user@jabber.server.com/resource' type='result'>
    <query xmlns='http://jabber.org/protocol/disco#items'>
        <item jid='im.jabber.server.com' name='Instant Message Service'/>
        <item jid='conference.jabber.server.com' name='Chatroom Service'/>
    </query>
</iq>

or objective c:

- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
{

    if([iq isResultIQ])
    {
        if([iq elementForName:@"query" xmlns:@"http://jabber.org/protocol/disco#items"])
        {
            NSLog(@"Jabber Server's Capabilities: %@", [iq XMLString]);
        }
    }
}

Now for every item returned send IQ to your server for it's properties and figure out which one is type of conference, something like this:

<iq from='user@jabber.server.com/resource' id='some_expression' to='conference.jabber.server.com' type='get'>
    <query xmlns='http://jabber.org/protocol/disco#info'/>
</iq>

or in objective c:

- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
{

    if([iq isResultIQ])
    {
        if([iq elementForName:@"query" xmlns:@"http://jabber.org/protocol/disco#items"])
        {
            NSXMLElement *query = [iq childElement];
            NSArray *items = [query children];
            for(NSXMLElement *item in items)
            {
                NSError *error = nil;
                NSXMLElement *sendQuery = [[NSXMLElement alloc] initWithXMLString:@"<query xmlns='http://jabber.org/protocol/disco#info'/>" 
                                                                            error:&error];
                XMPPIQ *sendIQ = [XMPPIQ iqWithType:@"get" 
                                                 to:[XMPPJID jidWithString:[item attributeStringValueForName:@"jid"]] 
                                          elementID:[xmppStream generateUUID] 
                                              child:sendQuery];
                [xmppStream sendElement:sendIQ];
            }
        }
    }
}

Listen for responses from server:

<iq from='conference.jabber.server.com' id='some_expression' to='user@jabber.server.com/resource' type='result'>
    <query xmlns='http://jabber.org/protocol/disco#info'>
        <identity category='conference' name='Server Group Chat Service' type='text'/>
        <feature var='http://jabber.org/protocol/muc'/>
    </query>
</iq>

and take group chat domain from identity with category:conference

- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
{

    if([iq isResultIQ])
    {
        if([iq elementForName:@"query" xmlns:@"http://jabber.org/protocol/disco#items"])
        {
            ...
        }
        else if([iq elementForName:@"query" xmlns:@"http://jabber.org/protocol/disco#info"])
        {
            NSXMLElement *query = [iq childElement];
            NSXMLElement *identity = [query elementForName:@"identity"];
            if([[identity attributeStringValueForName:@"category"] isEqualToString:@"conference"])
            {
                groupChatDomain = [iq fromStr];
            }
        }
    }
}

Finally, when we got group chat domain we can create chat room something like this:

XMPPJID *chatRoomJID = [XMPPJID jidWithUser:@"chat_room" 
                                     domain:groupChatDomain 
                                   resource:@"user"];
XMPPRoomMemoryStorage *roomMemoryStorage = [[XMPPRoomMemoryStorage alloc] init];
XMPPRoom *xmppRoom = [[XMPPRoom alloc] initWithRoomStorage:roomMemoryStorage
                                                       jid:roomChatJID
                                             dispatchQueue:dispatch_get_main_queue()];
[xmppRoom activate:xmppStream];
[xmppRoom addDelegate:self delegateQueue:dispatch_get_main_queue()];
[xmppRoom joinRoomUsingNickname:user history:nil];

and add <XMPPRoomDelegate> protocol in your view controller and its delegates:

- (void)xmppRoomDidCreate:(XMPPRoom *)sender
- (void)xmppRoomDidDestroy:(XMPPRoom *)sender
- (void)xmppRoom:(XMPPRoom *)sender didConfigure:(XMPPIQ *)iqResult
- (void)xmppRoom:(XMPPRoom *)sender didNotConfigure:(XMPPIQ *)iqResult
- (void)xmppRoomDidJoin:(XMPPRoom *)sender
- (void)xmppRoomDidLeave:(XMPPRoom *)sender
- (void)xmppRoom:(XMPPRoom *)sender occupantDidJoin:(XMPPJID *)occupantJID withPresence:(XMPPPresence *)presence
- (void)xmppRoom:(XMPPRoom *)sender occupantDidLeave:(XMPPJID *)occupantJID withPresence:(XMPPPresence *)presence
- (void)xmppRoom:(XMPPRoom *)sender didReceiveMessage:(XMPPMessage *)message fromOccupant:(XMPPJID *)occupantJID

Note: Before inviting other users to Chat Room, you have to send and confirm room configurations (other users can be invited but messages can not be sent).
So you can do this after Room is created (delegate - (void)xmppRoomDidCreate:(XMPPRoom *)sender is called) or your user has joined (delegate - (void)xmppRoomDidJoin:(XMPPRoom *)sender is called) to Chat Room.

To send and confirm room configuration do one of the following:

- (void)xmppRoomDidCreate:(XMPPRoom *)sender
{
    [sender configureRoomUsingOptions:nil];
}

or

- (void)xmppRoomDidJoin:(XMPPRoom *)sender
{    
    [sender configureRoomUsingOptions:nil];
}

Send nil to accept default options or you can send IQ with syntax as below to your server:

<iq type='set' from='user@jabber.server.com/resource' id='some_expression' to='chat_room@conference.jabber.server.com'>
    <query xmlns='http://jabber.org/protocol/muc#owner'>
        <x xmlns='jabber:x:data' type='submit'>
            <field var='FORM_TYPE'>
                <value>http://jabber.org/protocol/muc#roomconfig</value>
            </field>
            <field var='muc#roomconfig_roomname'>
                <value>My Chat Room</value>
            </field>
              .
              .
              .
        <x>
</query>
</iq>

or objective c code:

NSError *error = nil;
NSXMLElement *query = [[NSXMLElement alloc] initWithXMLString:@"<query xmlns='http://jabber.org/protocol/muc#owner'/>" 
                                                        error:&error];
NSXMLElement *x = [NSXMLElement elementWithName:@"x" 
                                          xmlns:@"jabber:x:data"];
[x addAttributeWithName:@"type" stringValue:@"submit"];
NSXMLElement *field1 = [NSXMLElement elementWithName:@"field"];
[field1 addAttributeWithName:@"var" stringValue:@"FORM_TYPE"];
NSXMLElement *value1 = [NSXMLElement elementWithName:@"value" 
                                         stringValue:@"http://jabber.org/protocol/muc#roomconfig"];
[field1 addChild:value1];

NSXMLElement *field2 = [NSXMLElement elementWithName:@"field"];
[field2 addAttributeWithName:@"var" stringValue:@"muc#roomconfig_roomname"];
NSXMLElement *value2 = [NSXMLElement elementWithName:@"value" 
                                         stringValue:@"My Chat Room"];
[field2 addChild:value2];

//Add other fields you need, just like field1 and field2

[x addChild:field1];
[x addChild:field2];

[query addChild:x];

NSXMLElement *roomOptions = [NSXMLElement elementWithName:@"iq"];
[roomOptions addAttributeWithName:@"type" stringValue:@"set"];
[roomOptions addAttributeWithName:@"id" stringValue:[xmppStream generateUUID];
[roomOptions addAttributeWithName:@"to" stringValue:@"chat_room@conference.jabber.server.com"];

[roomOptions addChild:query];

[sender configureRoomUsingOptions:roomOptions];

and list of all possible Configuration Form fields is here


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...