From dde9ac7d85b81812c49a3a03d7b61becc7a9d4f7 Mon Sep 17 00:00:00 2001 From: Jonathan Schleifer Date: Tue, 8 Feb 2011 20:45:21 +0100 Subject: [PATCH] Initial commit. Work done by me and Florian Zeitz. --- XMPPConnection.h | 43 ++++++++++ XMPPConnection.m | 200 +++++++++++++++++++++++++++++++++++++++++++++++ test.m | 33 ++++++++ 3 files changed, 276 insertions(+) create mode 100644 XMPPConnection.h create mode 100644 XMPPConnection.m create mode 100644 test.m diff --git a/XMPPConnection.h b/XMPPConnection.h new file mode 100644 index 0000000..5083b1e --- /dev/null +++ b/XMPPConnection.h @@ -0,0 +1,43 @@ +#import + +@class XMPPConnection; +@class XMPPIQ; +@class XMPPMessage; +@class XMPPPresence; + +@protocol XMPPConnectionDelegate +- (void)connectionWasClosed: (XMPPConnection*)conn; +- (void)connection: (XMPPConnection*)conn + didReceiveIQ: (XMPPIQ*)iq; +- (void)connection: (XMPPConnection*)conn + didReceivePresence: (XMPPPresence*)pres; +- (void)connection: (XMPPConnection*)conn + didReceiveMessage: (XMPPMessage*)msg; +@end + +@interface XMPPConnection: OFObject +{ + OFTCPSocket *sock; + OFXMLParser *parser; + OFXMLElementBuilder *elementBuilder; + OFString *username; + OFString *password; + OFString *server; + OFString *resource; + short port; + BOOL useTLS; + id delegate; + OFMutableArray *mechanisms; +} + +@property (copy) OFString *username; +@property (copy) OFString *password; +@property (copy) OFString *server; +@property (copy) OFString *resource; +@property (assign) short port; +@property (assign) BOOL useTLS; +@property (retain) id delegate; + +- (void)connect; +- (void)handleConnection; +@end diff --git a/XMPPConnection.m b/XMPPConnection.m new file mode 100644 index 0000000..2188ad3 --- /dev/null +++ b/XMPPConnection.m @@ -0,0 +1,200 @@ +#import "XMPPConnection.h" + +#define NS_BIND @"urn:ietf:params:xml:ns:xmpp-bind" +#define NS_CLIENT @"jabber:client" +#define NS_SASL @"urn:ietf:params:xml:ns:xmpp-sasl" +#define NS_STREAM @"http://etherx.jabber.org/streams" + +@implementation XMPPConnection +@synthesize username; +@synthesize password; +@synthesize server; +@synthesize resource; +@synthesize port; +@synthesize useTLS; +@synthesize delegate; + +- init +{ + self = [super init]; + + sock = [[OFTCPSocket alloc] init]; + parser = [[OFXMLParser alloc] init]; + elementBuilder = [[OFXMLElementBuilder alloc] init]; + + port = 5222; + useTLS = YES; + + mechanisms = [[OFMutableArray alloc] init]; + + parser.delegate = self; + elementBuilder.delegate = self; + + return self; +} + +- (void)dealloc +{ + [sock release]; + [parser release]; + [elementBuilder release]; + + [super dealloc]; +} + +- (void)_startStream +{ + [sock writeFormat: @"\n" + @"", server]; +} + +- (void)connect +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + + [sock connectToService: [OFString stringWithFormat: @"%u", port] + onNode: server]; + [self _startStream]; + + [pool release]; +} + +- (void)handleConnection +{ + char buf[512]; + + for (;;) { + size_t len = [sock readNBytes: 512 + intoBuffer: buf]; + + if (len < 1) + [delegate connectionWasClosed: self]; + + [of_stdout writeNBytes: len + fromBuffer: buf]; + [parser parseBuffer: buf + withSize: len]; + } +} + +- (void)parser: (OFXMLParser*)p + didStartElement: (OFString*)name + withPrefix: (OFString*)prefix + namespace: (OFString*)ns + attributes: (OFArray*)attrs +{ + if (![name isEqual: @"stream"] || ![prefix isEqual: @"stream"] || + ![ns isEqual: NS_STREAM]) + of_log(@"Did not get expected stream start!"); + + for (OFXMLAttribute *attr in attrs) + if ([attr.name isEqual: @"from"] && + ![attr.stringValue isEqual: server]) + of_log(@"Got invalid from in stream start!"); + + parser.delegate = elementBuilder; +} + +- (void)_addAuthMechanisms: (OFXMLElement*)mechanisms_ +{ + for (OFXMLElement *mechanism in mechanisms_.children) + [mechanisms addObject: + [mechanism.children.firstObject stringValue]]; +} + +- (void)_sendPLAINAuth +{ + OFXMLElement *authTag; + OFDataArray *message; + + message = [OFDataArray dataArrayWithItemSize: 1]; + /* XXX: authzid would go here */ + //[message addItem: authzid]; + /* separator */ + [message addItem: ""]; + /* authcid */ + [message addNItems: [username cStringLength] + fromCArray: [username cString]]; + /* separator */ + [message addItem: ""]; + /* passwd */ + [message addNItems: [password cStringLength] + fromCArray: [password cString]]; + + authTag = [OFXMLElement elementWithName: @"auth" + namespace: NS_SASL]; + [authTag addAttributeWithName: @"mechanism" + stringValue: @"PLAIN"]; + [authTag addChild: [OFXMLElement elementWithCharacters: + [message stringByBase64Encoding]]]; + + [sock writeString: [authTag stringValue]]; +} + +- (void)_sendResourceBind +{ + OFXMLElement *iq = [OFXMLElement elementWithName: @"iq"]; + [iq addAttributeWithName: @"type" stringValue: @"set"]; + [iq addChild: [OFXMLElement elementWithName: @"bind" + namespace: NS_BIND]]; + + [sock writeString: [iq stringValue]]; +} + +- (void)_handleFeatures: (OFXMLElement*)elem +{ + for (OFXMLElement *child in elem.children) { + if ([[child name] isEqual: @"mechanisms"] && + [[child namespace] isEqual: NS_SASL]) + [self _addAuthMechanisms: child]; + else if ([[child name] isEqual: @"bind"] && + [[child namespace] isEqual: NS_BIND]) + [self _sendResourceBind]; + } + + if ([mechanisms containsObject: @"PLAIN"]) + [self _sendPLAINAuth]; +} + +- (void)elementBuilder: (OFXMLElementBuilder*)b + didBuildElement: (OFXMLElement*)elem +{ + elem.defaultNamespace = NS_CLIENT; + [elem setPrefix: @"stream" + forNamespace: NS_STREAM]; + + if ([elem.name isEqual: @"features"] && + [elem.namespace isEqual: NS_STREAM]) { + [self _handleFeatures: elem]; + return; + } + + if ([elem.namespace isEqual: NS_SASL]) { + if ([elem.name isEqual: @"success"]) { + of_log(@"Auth successful"); + + /* Stream restart */ + [mechanisms release]; + mechanisms = [[OFMutableArray alloc] init]; + parser.delegate = self; + + [self _startStream]; + return; + } + + if ([elem.name isEqual: @"failure"]) + of_log(@"Auth failed!"); + // FIXME: Handle! + } +} + +- (void)elementBuilder: (OFXMLElementBuilder*)b + didNotExpectCloseTag: (OFString*)name + withPrefix: (OFString*)prefix + namespace: (OFString*)ns +{ + // TODO +} +@end diff --git a/test.m b/test.m new file mode 100644 index 0000000..7c6346b --- /dev/null +++ b/test.m @@ -0,0 +1,33 @@ +#include +#import "XMPPConnection.h" + +@interface AppDelegate: OFObject +{ + XMPPConnection *conn; +} +@end + +OF_APPLICATION_DELEGATE(AppDelegate) + +@implementation AppDelegate +- (void)applicationDidFinishLaunching +{ + OFArray *arguments = [OFApplication arguments]; + + conn = [[XMPPConnection alloc] init]; + + if (arguments.count != 3) { + of_log(@"Invalid count of command line arguments!"); + [OFApplication terminateWithStatus: 1]; + } + + [conn setServer: [arguments objectAtIndex: 0]]; + [conn setUsername: [arguments objectAtIndex: 1]]; + [conn setPassword: [arguments objectAtIndex: 2]]; + [conn setResource: @"ObjXMPP"]; + [conn setUseTLS: NO]; + + [conn connect]; + [conn handleConnection]; +} +@end