@@ -42,10 +42,23 @@ #define NS_CLIENT @"jabber:client" #define NS_SASL @"urn:ietf:params:xml:ns:xmpp-sasl" #define NS_STARTTLS @"urn:ietf:params:xml:ns:xmpp-tls" #define NS_SESSION @"urn:ietf:params:xml:ns:xmpp-session" #define NS_STREAM @"http://etherx.jabber.org/streams" + +@interface XMPPConnection () +- (void)XMPP_startStream; +- (void)XMPP_sendAuth: (OFString*)name; +- (void)XMPP_sendResourceBind; +- (void)XMPP_sendSession; +- (void)XMPP_handleResourceBind: (XMPPIQ*)iq; +- (void)XMPP_handleSession; +- (void)XMPP_handleFeatures: (OFXMLElement*)elem; +- (void)XMPP_handleIQ: (XMPPIQ*)iq; +- (void)XMPP_handleMessage: (XMPPMessage*)msg; +- (void)XMPP_handlePresence: (XMPPPresence*)pres; +@end @implementation XMPPConnection @synthesize username, password, server, resource, JID, port, useTLS, delegate; - init @@ -163,25 +176,17 @@ } [old release]; } -- (void)_startStream -{ - [sock writeFormat: @"\n" - @"", server]; -} - - (void)connect { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; [sock connectToHost: server onPort: port]; - [self _startStream]; + [self XMPP_startStream]; [pool release]; } - (void)handleConnection @@ -228,166 +233,10 @@ } parser.delegate = elementBuilder; } -- (void)_sendAuth: (OFString*)name -{ - OFXMLElement *authTag; - - authTag = [OFXMLElement elementWithName: @"auth" - namespace: NS_SASL]; - [authTag addAttributeWithName: @"mechanism" - stringValue: name]; - [authTag addChild: [OFXMLElement elementWithCharacters: - [[authModule clientFirstMessage] stringByBase64Encoding]]]; - - [self sendStanza: authTag]; -} - -- (void)_sendResourceBind -{ - XMPPIQ *iq = [XMPPIQ IQWithType: @"set" - ID: @"bind0"]; - OFXMLElement *bind = [OFXMLElement elementWithName: @"bind" - namespace: NS_BIND]; - if (resource) - [bind addChild: [OFXMLElement elementWithName: @"resource" - stringValue: resource]]; - [iq addChild: bind]; - - [self sendStanza: iq]; -} - -- (void)_sendSession -{ - XMPPIQ *iq = [XMPPIQ IQWithType: @"set" - ID: @"session0"]; - [iq addChild: [OFXMLElement elementWithName: @"session" - namespace: NS_SESSION]]; - [self sendStanza: iq]; -} - -- (void)_handleResourceBind: (XMPPIQ*)iq -{ - OFXMLElement *bindElem = iq.children.firstObject; - OFXMLElement *jidElem; - - if (![bindElem.name isEqual: @"bind"] || - ![bindElem.namespace isEqual: NS_BIND]) - assert(0); - - jidElem = bindElem.children.firstObject; - JID = [[XMPPJID alloc] initWithString: - [jidElem.children.firstObject stringValue]]; - - if (needsSession) { - [self _sendSession]; - return; - } - - if ([delegate respondsToSelector: @selector(connection:wasBoundToJID:)]) - [delegate connection: self - wasBoundToJID: JID]; -} - -- (void)_handleSession -{ - if ([delegate respondsToSelector: @selector(connection:wasBoundToJID:)]) - [delegate connection: self - wasBoundToJID: JID]; -} - -- (void)_handleFeatures: (OFXMLElement*)elem -{ - OFXMLElement *starttls = [elem - elementsForName: @"starttls" - namespace: NS_STARTTLS].firstObject; - OFXMLElement *bind = [elem elementsForName: @"bind" - namespace: NS_BIND].firstObject; - OFXMLElement *session = [elem elementsForName: @"session" - namespace: NS_SESSION].firstObject; - OFArray *mechs = [elem elementsForName: @"mechanisms" - namespace: NS_SASL]; - OFMutableArray *mechanisms = [OFMutableArray array]; - - if (starttls != nil) { - [self sendStanza: [OFXMLElement elementWithName: @"starttls" - namespace: NS_STARTTLS]]; - return; - } - - if ([mechs count] > 0) { - for (OFXMLElement *mech in [mechs.firstObject children]) - [mechanisms addObject: - [mech.children.firstObject stringValue]]; - - if ([mechanisms containsObject: @"SCRAM-SHA-1"]) { - authModule = [[XMPPSCRAMAuth alloc] - initWithAuthcid: username - password: password - hash: [OFSHA1Hash class]]; - [self _sendAuth: @"SCRAM-SHA-1"]; - return; - } - - if ([mechanisms containsObject: @"PLAIN"]) { - authModule = [[XMPPPLAINAuth alloc] - initWithAuthcid: username - password: password]; - [self _sendAuth: @"PLAIN"]; - return; - } - - assert(0); - } - - if (session != nil) - needsSession = YES; - - if (bind != nil) { - [self _sendResourceBind]; - return; - } - - assert(0); -} - -- (void)_handleIQ: (XMPPIQ*)iq -{ - // FIXME: More checking! - if ([iq.ID isEqual: @"bind0"] && [iq.type isEqual: @"result"]) { - [self _handleResourceBind: iq]; - return; - } - - if ([iq.ID isEqual: @"session0"] && [iq.type isEqual: @"result"]) { - [self _handleSession]; - return; - } - - if ([delegate respondsToSelector: @selector(connection:didReceiveIQ:)]) - [delegate connection: self - didReceiveIQ: iq]; -} - -- (void)_handleMessage: (XMPPMessage*)msg -{ - if ([delegate respondsToSelector: - @selector(connection:didReceiveMessage:)]) - [delegate connection: self - didReceiveMessage: msg]; -} - -- (void)_handlePresence: (XMPPPresence*)pres -{ - if ([delegate respondsToSelector: - @selector(connection:didReceivePresence:)]) - [delegate connection: self - didReceivePresence: pres]; -} - - (void)elementBuilder: (OFXMLElementBuilder*)b didBuildElement: (OFXMLElement*)elem { elem.defaultNamespace = NS_CLIENT; [elem setPrefix: @"stream" @@ -395,32 +244,32 @@ of_log(@"In: %@", elem); if ([elem.namespace isEqual: NS_CLIENT]) { if ([elem.name isEqual: @"iq"]) { - [self _handleIQ: [XMPPIQ stanzaWithElement: elem]]; + [self XMPP_handleIQ: [XMPPIQ stanzaWithElement: elem]]; return; } if ([elem.name isEqual: @"message"]) { - [self _handleMessage: + [self XMPP_handleMessage: [XMPPMessage stanzaWithElement: elem]]; return; } if ([elem.name isEqual: @"presence"]) { - [self _handlePresence: + [self XMPP_handlePresence: [XMPPPresence stanzaWithElement: elem]]; return; } assert(0); } if ([elem.namespace isEqual: NS_STREAM]) { if ([elem.name isEqual: @"features"]) { - [self _handleFeatures: elem]; + [self XMPP_handleFeatures: elem]; return; } assert(0); } @@ -430,11 +279,11 @@ /* FIXME: Catch errors here */ sock = [[GTLSSocket alloc] initWithSocket: sock]; /* Stream restart */ parser.delegate = self; - [self _startStream]; + [self XMPP_startStream]; return; } if ([elem.name isEqual: @"failure"]) /* TODO: Find/create an exception to throw here */ @@ -472,11 +321,11 @@ @selector(connectionWasAuthenticated:)]) [delegate connectionWasAuthenticated: self]; /* Stream restart */ parser.delegate = self; - [self _startStream]; + [self XMPP_startStream]; return; } if ([elem.name isEqual: @"failure"]) { of_log(@"Auth failed!"); @@ -498,6 +347,170 @@ withPrefix: (OFString*)prefix namespace: (OFString*)ns { // TODO } + +- (void)XMPP_startStream +{ + [sock writeFormat: @"\n" + @"", server]; +} + +- (void)XMPP_handleIQ: (XMPPIQ*)iq +{ + // FIXME: More checking! + if ([iq.ID isEqual: @"bind0"] && [iq.type isEqual: @"result"]) { + [self XMPP_handleResourceBind: iq]; + return; + } + + if ([iq.ID isEqual: @"session0"] && [iq.type isEqual: @"result"]) { + [self XMPP_handleSession]; + return; + } + + if ([delegate respondsToSelector: @selector(connection:didReceiveIQ:)]) + [delegate connection: self + didReceiveIQ: iq]; +} + +- (void)XMPP_handleMessage: (XMPPMessage*)msg +{ + if ([delegate respondsToSelector: + @selector(connection:didReceiveMessage:)]) + [delegate connection: self + didReceiveMessage: msg]; +} + +- (void)XMPP_handlePresence: (XMPPPresence*)pres +{ + if ([delegate respondsToSelector: + @selector(connection:didReceivePresence:)]) + [delegate connection: self + didReceivePresence: pres]; +} + +- (void)XMPP_handleFeatures: (OFXMLElement*)elem +{ + OFXMLElement *starttls = + [elem elementsForName: @"starttls" + namespace: NS_STARTTLS].firstObject; + OFXMLElement *bind = [elem elementsForName: @"bind" + namespace: NS_BIND].firstObject; + OFXMLElement *session = [elem elementsForName: @"session" + namespace: NS_SESSION].firstObject; + OFArray *mechs = [elem elementsForName: @"mechanisms" + namespace: NS_SASL]; + OFMutableArray *mechanisms = [OFMutableArray array]; + + if (starttls != nil) { + [self sendStanza: [OFXMLElement elementWithName: @"starttls" + namespace: NS_STARTTLS]]; + return; + } + + if ([mechs count] > 0) { + for (OFXMLElement *mech in [mechs.firstObject children]) + [mechanisms addObject: + [mech.children.firstObject stringValue]]; + + if ([mechanisms containsObject: @"SCRAM-SHA-1"]) { + authModule = [[XMPPSCRAMAuth alloc] + initWithAuthcid: username + password: password + hash: [OFSHA1Hash class]]; + [self XMPP_sendAuth: @"SCRAM-SHA-1"]; + return; + } + + if ([mechanisms containsObject: @"PLAIN"]) { + authModule = [[XMPPPLAINAuth alloc] + initWithAuthcid: username + password: password]; + [self XMPP_sendAuth: @"PLAIN"]; + return; + } + + assert(0); + } + + if (session != nil) + needsSession = YES; + + if (bind != nil) { + [self XMPP_sendResourceBind]; + return; + } + + assert(0); +} + +- (void)XMPP_sendAuth: (OFString*)name +{ + OFXMLElement *authTag; + + authTag = [OFXMLElement elementWithName: @"auth" + namespace: NS_SASL]; + [authTag addAttributeWithName: @"mechanism" + stringValue: name]; + [authTag addChild: [OFXMLElement elementWithCharacters: + [[authModule clientFirstMessage] stringByBase64Encoding]]]; + + [self sendStanza: authTag]; +} + +- (void)XMPP_sendResourceBind +{ + XMPPIQ *iq = [XMPPIQ IQWithType: @"set" + ID: @"bind0"]; + OFXMLElement *bind = [OFXMLElement elementWithName: @"bind" + namespace: NS_BIND]; + if (resource) + [bind addChild: [OFXMLElement elementWithName: @"resource" + stringValue: resource]]; + [iq addChild: bind]; + + [self sendStanza: iq]; +} + +- (void)XMPP_handleResourceBind: (XMPPIQ*)iq +{ + OFXMLElement *bindElem = iq.children.firstObject; + OFXMLElement *jidElem; + + if (![bindElem.name isEqual: @"bind"] || + ![bindElem.namespace isEqual: NS_BIND]) + assert(0); + + jidElem = bindElem.children.firstObject; + JID = [[XMPPJID alloc] initWithString: + [jidElem.children.firstObject stringValue]]; + + if (needsSession) { + [self XMPP_sendSession]; + return; + } + + if ([delegate respondsToSelector: @selector(connection:wasBoundToJID:)]) + [delegate connection: self + wasBoundToJID: JID]; +} + +- (void)XMPP_sendSession +{ + XMPPIQ *iq = [XMPPIQ IQWithType: @"set" + ID: @"session0"]; + [iq addChild: [OFXMLElement elementWithName: @"session" + namespace: NS_SESSION]]; + [self sendStanza: iq]; +} + +- (void)XMPP_handleSession +{ + if ([delegate respondsToSelector: @selector(connection:wasBoundToJID:)]) + [delegate connection: self + wasBoundToJID: JID]; +} @end