Index: Makefile ================================================================== --- Makefile +++ Makefile @@ -1,4 +1,4 @@ -all: tests +all: tests/tests -tests: test.m XMPPConnection.m XMPPStanza.m - objfw-compile -o $@ $^ -lidn -Wall -Werror +tests/tests: tests/test.m src/XMPPConnection.m src/XMPPStanza.m + objfw-compile -o $@ $^ -lidn -Wall -Werror -Isrc DELETED XMPPConnection.h Index: XMPPConnection.h ================================================================== --- XMPPConnection.h +++ XMPPConnection.h @@ -1,44 +0,0 @@ -#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; -- (void)sendStanza: (OFXMLElement*)elem; -@end DELETED XMPPConnection.m Index: XMPPConnection.m ================================================================== --- XMPPConnection.m +++ XMPPConnection.m @@ -1,238 +0,0 @@ -#include -#include -#import "XMPPConnection.h" -#import "XMPPStanza.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)setUsername: (OFString*)username_ -{ - OFString *old = username; - char *node; - - Stringprep_rc rc; - if ((rc = stringprep_profile([username_ cString], &node, "Nodeprep", 0)) - != STRINGPREP_OK) { - of_log(@"Nodeprep failed: %s", stringprep_strerror(rc)); - assert(0); - } - - @try { - username = [[OFString alloc] initWithCString: node]; - } @finally { - free(node); - } - - [old release]; -} - -- (void)_startStream -{ - [sock writeFormat: @"\n" - @"", server]; -} - -- (void)connect -{ - OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; - - [sock connectToHost: server onPort: port]; - [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)sendStanza: (OFXMLElement*)elem -{ - [sock writeString: [elem stringValue]]; -} - -- (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]]]; - - [self sendStanza: authTag]; -} - -- (void)_sendResourceBind -{ - XMPPIQ *iq = [XMPPIQ IQWithType: @"set" ID: @"bind0"]; - [iq addChild: [OFXMLElement elementWithName: @"bind" - namespace: NS_BIND]]; - - [self sendStanza: iq]; -} - -- (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! - } - - if ([elem.name isEqual: @"iq"] && - [elem.namespace isEqual: NS_CLIENT]) { - XMPPIQ *iq = [XMPPIQ stanzaWithElement: elem]; - if ([iq.ID isEqual: @"bind0"] && [iq.type isEqual: @"result"]) { - OFXMLElement *bindElem = iq.children.firstObject; - OFXMLElement *jidElem = bindElem.children.firstObject; - of_log(@"Bound to JID: %@", - [jidElem.children.firstObject stringValue]); - } - } -} - -- (void)elementBuilder: (OFXMLElementBuilder*)b - didNotExpectCloseTag: (OFString*)name - withPrefix: (OFString*)prefix - namespace: (OFString*)ns -{ - // TODO -} -@end DELETED XMPPStanza.h Index: XMPPStanza.h ================================================================== --- XMPPStanza.h +++ XMPPStanza.h @@ -1,86 +0,0 @@ -#import - -@interface XMPPStanza: OFXMLElement -{ - OFString *from; - OFString *to; - OFString *type; - OFString *ID; -} - -@property (copy) OFString *from; -@property (copy) OFString *to; -@property (copy) OFString *type; -@property (copy) OFString *ID; - -+ stanzaWithName: (OFString*)name; -+ stanzaWithName: (OFString*)name - type: (OFString*)type_; -+ stanzaWithName: (OFString*)name - ID: (OFString*)ID_; -+ stanzaWithName: (OFString*)name - type: (OFString*)type_ - ID: (OFString*)ID_; -+ stanzaWithElement: (OFXMLElement*)elem; - -- initWithName: (OFString*)name; -- initWithName: (OFString*)name - type: (OFString*)type_; -- initWithName: (OFString*)name - ID: (OFString*)ID_; -- initWithName: (OFString*)name - type: (OFString*)type_ - ID: (OFString*)ID_; -- initWithElement: (OFXMLElement*)elem; -@end - -@interface XMPPIQ: XMPPStanza -{ -} - -+ IQWithType: (OFString*)type_ - ID: (OFString*)ID_; - -- initWithType: (OFString*)type_ - ID: (OFString*)ID_; -@end - -@interface XMPPMessage: XMPPStanza -{ -} - -+ message; -+ messageWithID: (OFString*)ID_; -+ messageWithType: (OFString*)type_; -+ messageWithType: (OFString*)type_ - ID: (OFString*)ID_; - -- init; -- initWithID: (OFString*)ID_; -- initWithType: (OFString*)type_; -- initWithType: (OFString*)type_ - ID: (OFString*)ID_; - -- (void)addBody: (OFString*)body; -@end - -@interface XMPPPresence: XMPPStanza -{ -} - -+ presence; -+ presenceWithID: (OFString*)ID_; -+ presenceWithType: (OFString*)type_; -+ presenceWithType: (OFString*)type_ - ID: (OFString*)ID_; - -- init; -- initWithID: (OFString*)ID_; -- initWithType: (OFString*)type_; -- initWithType: (OFString*)type_ - ID: (OFString*)ID_; - -- (void)addShow: (OFString*)show; -- (void)addStatus: (OFString*)status; -- (void)addPriority: (int8_t)priority; -@end DELETED XMPPStanza.m Index: XMPPStanza.m ================================================================== --- XMPPStanza.m +++ XMPPStanza.m @@ -1,303 +0,0 @@ -#import "XMPPStanza.h" - -@implementation XMPPStanza -@synthesize from; -@synthesize to; -@synthesize type; -@synthesize ID; - -+ stanzaWithName: (OFString*)name -{ - return [[[self alloc] initWithName: name] autorelease]; -} - -+ stanzaWithName: (OFString*)name - type: (OFString*)type_ -{ - return [[[self alloc] initWithName: name - type: type_] autorelease]; -} - -+ stanzaWithName: (OFString*)name - ID: (OFString*)ID_ -{ - return [[[self alloc] initWithName: name - ID: ID_] autorelease]; -} - -+ stanzaWithName: (OFString*)name - type: (OFString*)type_ - ID: (OFString*)ID_ -{ - return [[[self alloc] initWithName: name - type: type_ - ID: ID_] autorelease]; -} - -+ stanzaWithElement: (OFXMLElement*)elem { - return [[[self alloc] initWithElement: elem] autorelease]; -} - -- initWithName: (OFString*)name_ -{ - return [self initWithName: name_ - type: nil - ID: nil]; -} - -- initWithName: (OFString*)name_ - type: (OFString*)type_ -{ - return [self initWithName: name_ - type: type_ - ID: nil]; -} - -- initWithName: (OFString*)name_ - ID: (OFString*)ID_ -{ - return [self initWithName: name_ - type: nil - ID: ID_]; -} - -- initWithName: (OFString*)name_ - type: (OFString*)type_ - ID: (OFString*)ID_ -{ - if (!([name_ isEqual: @"iq"] || - [name_ isEqual: @"message"] || - [name_ isEqual: @"presence"])) - of_log(@"Invalid stanza name!"); - - id ret; - ret = [super initWithName: name_]; - [self setDefaultNamespace: @"jabber:client"]; - if (type_) - [ret setType: type_]; - if (ID_) - [ret setID: ID_]; - return ret; -} - -- initWithElement: (OFXMLElement*)elem -{ - self = [super initWithName: elem.name - namespace: elem.namespace]; - - OFXMLAttribute *attr; - - for (attr in elem.attributes) { - if ([attr.name isEqual: @"from"]) { - [self setFrom: [attr stringValue]]; - } else if ([attr.name isEqual: @"to"]) { - [self setTo: [attr stringValue]]; - } else if ([attr.name isEqual: @"type"]) { - [self setType: [attr stringValue]]; - } else if ([attr.name isEqual: @"id"]) { - [self setID: [attr stringValue]]; - } else { - [self addAttribute: attr]; - } - } - - OFXMLElement *el; - - for (el in elem.children) { - [self addChild: el]; - } - - return self; -} - -- (void)dealloc -{ - [from release]; - [to release]; - [type release]; - [ID release]; - - [super dealloc]; -} - -- (void)setFrom: (OFString*)from_ -{ - OFString* old = from; - from = [from_ copy]; - [old release]; - [self addAttributeWithName: @"from" stringValue: from_]; -} - -- (void)setTo: (OFString*)to_ -{ - OFString* old = to; - to = [to_ copy]; - [old release]; - [self addAttributeWithName: @"to" stringValue: to]; -} - -- (void)setType: (OFString*)type_ -{ - OFString* old = type; - type = [type_ copy]; - [old release]; - [self addAttributeWithName: @"type" stringValue: type]; -} - -- (void)setID: (OFString*)ID_ -{ - OFString* old = ID; - ID = [ID_ copy]; - [old release]; - [self addAttributeWithName: @"id" - stringValue: ID]; -} -@end - -@implementation XMPPIQ -+ IQWithType: (OFString*)type_ - ID: (OFString*)ID_ -{ - return [[[self alloc] initWithType: type_ - ID: ID_] autorelease]; -} - -- initWithType: (OFString*)type_ - ID: (OFString*)ID_ -{ - if (!([type_ isEqual: @"get"] || - [type_ isEqual: @"set"] || - [type_ isEqual: @"result"] || - [type_ isEqual: @"error"])) - of_log(@"Invalid IQ type!"); - - return [super initWithName: @"iq" - type: type_ - ID: ID_]; -} -@end - -@implementation XMPPMessage -+ message -{ - return [[[self alloc] init] autorelease]; -} - -+ messageWithID: (OFString*)ID_ -{ - return [[[self alloc] initWithID: ID_] autorelease]; -} - -+ messageWithType: (OFString*)type_ -{ - return [[[self alloc] initWithType: type_] autorelease]; -} - -+ messageWithType: (OFString*)type_ - ID: (OFString*)ID_ -{ - return [[[self alloc] initWithType: type_ - ID: ID_] autorelease]; -} - -- init -{ - return [self initWithType: nil - ID: nil]; -} - -- initWithID: (OFString*)ID_ -{ - return [self initWithType: nil - ID: ID_]; -} - -- initWithType: (OFString*)type_ -{ - return [self initWithType: type_ - ID: nil]; -} - -- initWithType: (OFString*)type_ - ID: (OFString*)ID_ -{ - return [super initWithName: @"message" - type: type_ - ID: ID_]; -} - -- (void)addBody: (OFString*)body -{ - [self addChild: [OFXMLElement elementWithName: @"body" - stringValue: body]]; -} -@end - -@implementation XMPPPresence -+ presence -{ - return [[[self alloc] init] autorelease]; -} - -+ presenceWithID: (OFString*)ID_ -{ - return [[[self alloc] initWithID: ID_] autorelease]; -} - -+ presenceWithType: (OFString*)type_ -{ - return [[[self alloc] initWithType: type_] autorelease]; -} - -+ presenceWithType: (OFString*)type_ - ID: (OFString*)ID_ -{ - return [[[self alloc] initWithType: type_ - ID: ID_] autorelease]; -} - -- init -{ - return [self initWithType: nil - ID: nil]; -} - -- initWithID: (OFString*)ID_ -{ - return [self initWithType: nil - ID: ID_]; -} - -- initWithType: (OFString*)type_ -{ - return [self initWithType: type_ - ID: nil]; -} - -- initWithType: (OFString*)type_ - ID: (OFString*)ID_ -{ - return [super initWithName: @"presence" - type: type_ - ID: ID_]; -} - -- (void)addShow: (OFString*)show -{ - [self addChild: [OFXMLElement elementWithName: @"show" - stringValue: show]]; -} - -- (void)addStatus: (OFString*)status -{ - [self addChild: [OFXMLElement elementWithName: @"status" - stringValue: status]]; -} - -- (void)addPriority: (int8_t)priority -{ - OFString* prio = [OFString stringWithFormat: @"%" @PRId8, priority]; - [self addChild: [OFXMLElement elementWithName: @"priority" - stringValue: prio]]; -} -@end DELETED XMPPStanza.o Index: XMPPStanza.o ================================================================== --- XMPPStanza.o +++ XMPPStanza.o cannot compute difference between binary files ADDED src/XMPPConnection.h Index: src/XMPPConnection.h ================================================================== --- src/XMPPConnection.h +++ src/XMPPConnection.h @@ -0,0 +1,44 @@ +#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; +- (void)sendStanza: (OFXMLElement*)elem; +@end ADDED src/XMPPConnection.m Index: src/XMPPConnection.m ================================================================== --- src/XMPPConnection.m +++ src/XMPPConnection.m @@ -0,0 +1,238 @@ +#include +#include +#import "XMPPConnection.h" +#import "XMPPStanza.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)setUsername: (OFString*)username_ +{ + OFString *old = username; + char *node; + + Stringprep_rc rc; + if ((rc = stringprep_profile([username_ cString], &node, "Nodeprep", 0)) + != STRINGPREP_OK) { + of_log(@"Nodeprep failed: %s", stringprep_strerror(rc)); + assert(0); + } + + @try { + username = [[OFString alloc] initWithCString: node]; + } @finally { + free(node); + } + + [old release]; +} + +- (void)_startStream +{ + [sock writeFormat: @"\n" + @"", server]; +} + +- (void)connect +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + + [sock connectToHost: server onPort: port]; + [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)sendStanza: (OFXMLElement*)elem +{ + [sock writeString: [elem stringValue]]; +} + +- (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]]]; + + [self sendStanza: authTag]; +} + +- (void)_sendResourceBind +{ + XMPPIQ *iq = [XMPPIQ IQWithType: @"set" ID: @"bind0"]; + [iq addChild: [OFXMLElement elementWithName: @"bind" + namespace: NS_BIND]]; + + [self sendStanza: iq]; +} + +- (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! + } + + if ([elem.name isEqual: @"iq"] && + [elem.namespace isEqual: NS_CLIENT]) { + XMPPIQ *iq = [XMPPIQ stanzaWithElement: elem]; + if ([iq.ID isEqual: @"bind0"] && [iq.type isEqual: @"result"]) { + OFXMLElement *bindElem = iq.children.firstObject; + OFXMLElement *jidElem = bindElem.children.firstObject; + of_log(@"Bound to JID: %@", + [jidElem.children.firstObject stringValue]); + } + } +} + +- (void)elementBuilder: (OFXMLElementBuilder*)b + didNotExpectCloseTag: (OFString*)name + withPrefix: (OFString*)prefix + namespace: (OFString*)ns +{ + // TODO +} +@end ADDED src/XMPPStanza.h Index: src/XMPPStanza.h ================================================================== --- src/XMPPStanza.h +++ src/XMPPStanza.h @@ -0,0 +1,86 @@ +#import + +@interface XMPPStanza: OFXMLElement +{ + OFString *from; + OFString *to; + OFString *type; + OFString *ID; +} + +@property (copy) OFString *from; +@property (copy) OFString *to; +@property (copy) OFString *type; +@property (copy) OFString *ID; + ++ stanzaWithName: (OFString*)name; ++ stanzaWithName: (OFString*)name + type: (OFString*)type_; ++ stanzaWithName: (OFString*)name + ID: (OFString*)ID_; ++ stanzaWithName: (OFString*)name + type: (OFString*)type_ + ID: (OFString*)ID_; ++ stanzaWithElement: (OFXMLElement*)elem; + +- initWithName: (OFString*)name; +- initWithName: (OFString*)name + type: (OFString*)type_; +- initWithName: (OFString*)name + ID: (OFString*)ID_; +- initWithName: (OFString*)name + type: (OFString*)type_ + ID: (OFString*)ID_; +- initWithElement: (OFXMLElement*)elem; +@end + +@interface XMPPIQ: XMPPStanza +{ +} + ++ IQWithType: (OFString*)type_ + ID: (OFString*)ID_; + +- initWithType: (OFString*)type_ + ID: (OFString*)ID_; +@end + +@interface XMPPMessage: XMPPStanza +{ +} + ++ message; ++ messageWithID: (OFString*)ID_; ++ messageWithType: (OFString*)type_; ++ messageWithType: (OFString*)type_ + ID: (OFString*)ID_; + +- init; +- initWithID: (OFString*)ID_; +- initWithType: (OFString*)type_; +- initWithType: (OFString*)type_ + ID: (OFString*)ID_; + +- (void)addBody: (OFString*)body; +@end + +@interface XMPPPresence: XMPPStanza +{ +} + ++ presence; ++ presenceWithID: (OFString*)ID_; ++ presenceWithType: (OFString*)type_; ++ presenceWithType: (OFString*)type_ + ID: (OFString*)ID_; + +- init; +- initWithID: (OFString*)ID_; +- initWithType: (OFString*)type_; +- initWithType: (OFString*)type_ + ID: (OFString*)ID_; + +- (void)addShow: (OFString*)show; +- (void)addStatus: (OFString*)status; +- (void)addPriority: (int8_t)priority; +@end ADDED src/XMPPStanza.m Index: src/XMPPStanza.m ================================================================== --- src/XMPPStanza.m +++ src/XMPPStanza.m @@ -0,0 +1,303 @@ +#import "XMPPStanza.h" + +@implementation XMPPStanza +@synthesize from; +@synthesize to; +@synthesize type; +@synthesize ID; + ++ stanzaWithName: (OFString*)name +{ + return [[[self alloc] initWithName: name] autorelease]; +} + ++ stanzaWithName: (OFString*)name + type: (OFString*)type_ +{ + return [[[self alloc] initWithName: name + type: type_] autorelease]; +} + ++ stanzaWithName: (OFString*)name + ID: (OFString*)ID_ +{ + return [[[self alloc] initWithName: name + ID: ID_] autorelease]; +} + ++ stanzaWithName: (OFString*)name + type: (OFString*)type_ + ID: (OFString*)ID_ +{ + return [[[self alloc] initWithName: name + type: type_ + ID: ID_] autorelease]; +} + ++ stanzaWithElement: (OFXMLElement*)elem { + return [[[self alloc] initWithElement: elem] autorelease]; +} + +- initWithName: (OFString*)name_ +{ + return [self initWithName: name_ + type: nil + ID: nil]; +} + +- initWithName: (OFString*)name_ + type: (OFString*)type_ +{ + return [self initWithName: name_ + type: type_ + ID: nil]; +} + +- initWithName: (OFString*)name_ + ID: (OFString*)ID_ +{ + return [self initWithName: name_ + type: nil + ID: ID_]; +} + +- initWithName: (OFString*)name_ + type: (OFString*)type_ + ID: (OFString*)ID_ +{ + if (!([name_ isEqual: @"iq"] || + [name_ isEqual: @"message"] || + [name_ isEqual: @"presence"])) + of_log(@"Invalid stanza name!"); + + id ret; + ret = [super initWithName: name_]; + [self setDefaultNamespace: @"jabber:client"]; + if (type_) + [ret setType: type_]; + if (ID_) + [ret setID: ID_]; + return ret; +} + +- initWithElement: (OFXMLElement*)elem +{ + self = [super initWithName: elem.name + namespace: elem.namespace]; + + OFXMLAttribute *attr; + + for (attr in elem.attributes) { + if ([attr.name isEqual: @"from"]) { + [self setFrom: [attr stringValue]]; + } else if ([attr.name isEqual: @"to"]) { + [self setTo: [attr stringValue]]; + } else if ([attr.name isEqual: @"type"]) { + [self setType: [attr stringValue]]; + } else if ([attr.name isEqual: @"id"]) { + [self setID: [attr stringValue]]; + } else { + [self addAttribute: attr]; + } + } + + OFXMLElement *el; + + for (el in elem.children) { + [self addChild: el]; + } + + return self; +} + +- (void)dealloc +{ + [from release]; + [to release]; + [type release]; + [ID release]; + + [super dealloc]; +} + +- (void)setFrom: (OFString*)from_ +{ + OFString* old = from; + from = [from_ copy]; + [old release]; + [self addAttributeWithName: @"from" stringValue: from_]; +} + +- (void)setTo: (OFString*)to_ +{ + OFString* old = to; + to = [to_ copy]; + [old release]; + [self addAttributeWithName: @"to" stringValue: to]; +} + +- (void)setType: (OFString*)type_ +{ + OFString* old = type; + type = [type_ copy]; + [old release]; + [self addAttributeWithName: @"type" stringValue: type]; +} + +- (void)setID: (OFString*)ID_ +{ + OFString* old = ID; + ID = [ID_ copy]; + [old release]; + [self addAttributeWithName: @"id" + stringValue: ID]; +} +@end + +@implementation XMPPIQ ++ IQWithType: (OFString*)type_ + ID: (OFString*)ID_ +{ + return [[[self alloc] initWithType: type_ + ID: ID_] autorelease]; +} + +- initWithType: (OFString*)type_ + ID: (OFString*)ID_ +{ + if (!([type_ isEqual: @"get"] || + [type_ isEqual: @"set"] || + [type_ isEqual: @"result"] || + [type_ isEqual: @"error"])) + of_log(@"Invalid IQ type!"); + + return [super initWithName: @"iq" + type: type_ + ID: ID_]; +} +@end + +@implementation XMPPMessage ++ message +{ + return [[[self alloc] init] autorelease]; +} + ++ messageWithID: (OFString*)ID_ +{ + return [[[self alloc] initWithID: ID_] autorelease]; +} + ++ messageWithType: (OFString*)type_ +{ + return [[[self alloc] initWithType: type_] autorelease]; +} + ++ messageWithType: (OFString*)type_ + ID: (OFString*)ID_ +{ + return [[[self alloc] initWithType: type_ + ID: ID_] autorelease]; +} + +- init +{ + return [self initWithType: nil + ID: nil]; +} + +- initWithID: (OFString*)ID_ +{ + return [self initWithType: nil + ID: ID_]; +} + +- initWithType: (OFString*)type_ +{ + return [self initWithType: type_ + ID: nil]; +} + +- initWithType: (OFString*)type_ + ID: (OFString*)ID_ +{ + return [super initWithName: @"message" + type: type_ + ID: ID_]; +} + +- (void)addBody: (OFString*)body +{ + [self addChild: [OFXMLElement elementWithName: @"body" + stringValue: body]]; +} +@end + +@implementation XMPPPresence ++ presence +{ + return [[[self alloc] init] autorelease]; +} + ++ presenceWithID: (OFString*)ID_ +{ + return [[[self alloc] initWithID: ID_] autorelease]; +} + ++ presenceWithType: (OFString*)type_ +{ + return [[[self alloc] initWithType: type_] autorelease]; +} + ++ presenceWithType: (OFString*)type_ + ID: (OFString*)ID_ +{ + return [[[self alloc] initWithType: type_ + ID: ID_] autorelease]; +} + +- init +{ + return [self initWithType: nil + ID: nil]; +} + +- initWithID: (OFString*)ID_ +{ + return [self initWithType: nil + ID: ID_]; +} + +- initWithType: (OFString*)type_ +{ + return [self initWithType: type_ + ID: nil]; +} + +- initWithType: (OFString*)type_ + ID: (OFString*)ID_ +{ + return [super initWithName: @"presence" + type: type_ + ID: ID_]; +} + +- (void)addShow: (OFString*)show +{ + [self addChild: [OFXMLElement elementWithName: @"show" + stringValue: show]]; +} + +- (void)addStatus: (OFString*)status +{ + [self addChild: [OFXMLElement elementWithName: @"status" + stringValue: status]]; +} + +- (void)addPriority: (int8_t)priority +{ + OFString* prio = [OFString stringWithFormat: @"%" @PRId8, priority]; + [self addChild: [OFXMLElement elementWithName: @"priority" + stringValue: prio]]; +} +@end DELETED test.m Index: test.m ================================================================== --- test.m +++ test.m @@ -1,73 +0,0 @@ -#include -#import -#import "XMPPConnection.h" -#import "XMPPStanza.h" - -@interface AppDelegate: OFObject -{ - XMPPConnection *conn; -} -@end - -OF_APPLICATION_DELEGATE(AppDelegate) - -@implementation AppDelegate -- (void)applicationDidFinishLaunching -{ - OFArray *arguments = [OFApplication arguments]; - - XMPPPresence *pres = [XMPPPresence presence]; - [pres addShow: @"chat"]; - [pres addStatus: @"Bored"]; - [pres addPriority: 20]; - pres.to = @"alice@example.com"; - pres.from = @"bob@example.org"; - assert([[pres stringValue] isEqual: @"chat" - @"Bored20" - @""]); - - XMPPMessage *msg = [XMPPMessage messageWithType: @"chat"]; - [msg addBody: @"Hello everyone"]; - msg.to = @"jdev@conference.jabber.org"; - msg.from = @"alice@example.com"; - assert([[msg stringValue] isEqual: @"Hello everyone" - @""]); - - XMPPIQ *iq = [XMPPIQ IQWithType: @"set" ID: @"128"]; - iq.to = @"juliet@capulet.lit"; - iq.from = @"romeo@montague.lit"; - assert([[iq stringValue] isEqual: @""]); - - OFXMLElement *elem = [OFXMLElement elementWithName: @"iq"]; - [elem addAttributeWithName: @"from" stringValue: @"bob@localhost"]; - [elem addAttributeWithName: @"to" stringValue: @"alice@localhost"]; - [elem addAttributeWithName: @"type" stringValue: @"get"]; - [elem addAttributeWithName: @"id" stringValue: @"42"]; - XMPPStanza *stanza = [XMPPStanza stanzaWithElement: elem]; - assert([[elem stringValue] isEqual: [stanza stringValue]]); - assert(([[OFString stringWithFormat: @"%@, %@, %@, %@", stanza.from, - stanza.to, stanza.type, stanza.ID] - isEqual: @"bob@localhost, alice@localhost, get, 42"])); - - 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 ADDED tests/test.m Index: tests/test.m ================================================================== --- tests/test.m +++ tests/test.m @@ -0,0 +1,73 @@ +#include +#import +#import "XMPPConnection.h" +#import "XMPPStanza.h" + +@interface AppDelegate: OFObject +{ + XMPPConnection *conn; +} +@end + +OF_APPLICATION_DELEGATE(AppDelegate) + +@implementation AppDelegate +- (void)applicationDidFinishLaunching +{ + OFArray *arguments = [OFApplication arguments]; + + XMPPPresence *pres = [XMPPPresence presence]; + [pres addShow: @"chat"]; + [pres addStatus: @"Bored"]; + [pres addPriority: 20]; + pres.to = @"alice@example.com"; + pres.from = @"bob@example.org"; + assert([[pres stringValue] isEqual: @"chat" + @"Bored20" + @""]); + + XMPPMessage *msg = [XMPPMessage messageWithType: @"chat"]; + [msg addBody: @"Hello everyone"]; + msg.to = @"jdev@conference.jabber.org"; + msg.from = @"alice@example.com"; + assert([[msg stringValue] isEqual: @"Hello everyone" + @""]); + + XMPPIQ *iq = [XMPPIQ IQWithType: @"set" ID: @"128"]; + iq.to = @"juliet@capulet.lit"; + iq.from = @"romeo@montague.lit"; + assert([[iq stringValue] isEqual: @""]); + + OFXMLElement *elem = [OFXMLElement elementWithName: @"iq"]; + [elem addAttributeWithName: @"from" stringValue: @"bob@localhost"]; + [elem addAttributeWithName: @"to" stringValue: @"alice@localhost"]; + [elem addAttributeWithName: @"type" stringValue: @"get"]; + [elem addAttributeWithName: @"id" stringValue: @"42"]; + XMPPStanza *stanza = [XMPPStanza stanzaWithElement: elem]; + assert([[elem stringValue] isEqual: [stanza stringValue]]); + assert(([[OFString stringWithFormat: @"%@, %@, %@, %@", stanza.from, + stanza.to, stanza.type, stanza.ID] + isEqual: @"bob@localhost, alice@localhost, get, 42"])); + + 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