Add a basic folder structure
This commit is contained in:
parent
fb15fbd391
commit
12ec7d5f08
7 changed files with 3 additions and 3 deletions
44
src/XMPPConnection.h
Normal file
44
src/XMPPConnection.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
#import <ObjFW/ObjFW.h>
|
||||
|
||||
@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 <OFXMLElementBuilderDelegate>
|
||||
{
|
||||
OFTCPSocket *sock;
|
||||
OFXMLParser *parser;
|
||||
OFXMLElementBuilder *elementBuilder;
|
||||
OFString *username;
|
||||
OFString *password;
|
||||
OFString *server;
|
||||
OFString *resource;
|
||||
short port;
|
||||
BOOL useTLS;
|
||||
id <XMPPConnectionDelegate> 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 <XMPPConnectionDelegate> delegate;
|
||||
|
||||
- (void)connect;
|
||||
- (void)handleConnection;
|
||||
- (void)sendStanza: (OFXMLElement*)elem;
|
||||
@end
|
238
src/XMPPConnection.m
Normal file
238
src/XMPPConnection.m
Normal file
|
@ -0,0 +1,238 @@
|
|||
#include <stringprep.h>
|
||||
#include <assert.h>
|
||||
#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: @"<?xml version='1.0'?>\n"
|
||||
@"<stream:stream to='%@' xmlns='" NS_CLIENT @"' "
|
||||
@"xmlns:stream='" NS_STREAM @"' "
|
||||
@"version='1.0'>", 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
|
86
src/XMPPStanza.h
Normal file
86
src/XMPPStanza.h
Normal file
|
@ -0,0 +1,86 @@
|
|||
#import <ObjFW/ObjFW.h>
|
||||
|
||||
@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
|
303
src/XMPPStanza.m
Normal file
303
src/XMPPStanza.m
Normal file
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue