Index: src/XMPPConnection.h ================================================================== --- src/XMPPConnection.h +++ src/XMPPConnection.h @@ -27,10 +27,12 @@ #import "XMPPCallback.h" #import "XMPPStorage.h" OF_ASSUME_NONNULL_BEGIN +#define XMPP_CONNECTION_BUFFER_LENGTH 512 + @class XMPPConnection; @class XMPPJID; @class XMPPIQ; @class XMPPMessage; @class XMPPPresence; @@ -108,12 +110,14 @@ /*! * @brief This callback is called when the connection was closed. * * @param connection The connection that was closed + * @param error The error XML element the stream encountered or nil */ -- (void)connectionWasClosed: (XMPPConnection *)connection; +- (void)connectionWasClosed: (XMPPConnection *)connection + error: (nullable OFXMLElement *)error; /*! * @brief This callback is called when the connection threw an exception. * * @param connection The connection which threw an exception @@ -139,14 +143,14 @@ @end /*! * @brief A class which abstracts a connection to an XMPP service. */ -@interface XMPPConnection: OFObject +@interface XMPPConnection: OFObject { OF_KINDOF(OFTCPSocket *) _socket; + char _buffer[XMPP_CONNECTION_BUFFER_LENGTH]; OFXMLParser *_parser, *_oldParser; OFXMLElementBuilder *_elementBuilder, *_oldElementBuilder; OFString *_Nullable _username, *_Nullable _password, *_Nullable _server; OFString *_Nullable _resource; bool _usesAnonymousAuthentication; @@ -155,10 +159,11 @@ OFString *_Nullable _domain, *_Nullable _domainToASCII; XMPPJID *_Nullable _JID; uint16_t _port; id _Nullable _dataStorage; OFString *_Nullable _language; + OFMutableArray *_nextSRVRecords; XMPPMulticastDelegate *_delegates; OFMutableDictionary OF_GENERIC(OFString *, XMPPCallback *) *_callbacks; XMPPAuthenticator *_authModule; bool _streamOpen, _needsSession, _encryptionRequired, _encrypted; bool _supportsRosterVersioning, _supportsStreamManagement; Index: src/XMPPConnection.m ================================================================== --- src/XMPPConnection.m +++ src/XMPPConnection.m @@ -54,30 +54,15 @@ #import "namespaces.h" #import -#define BUFFER_LENGTH 512 - -@interface XMPPConnection () -- (void)xmpp_socketDidConnect: (OFTCPSocket *)socket - context: (OFArray *)nextSRVRecords - exception: (id)exception; -- (void)xmpp_tryNextSRVRecord: (OFArray *)SRVRecords; -- (void)xmpp_resolver: (OFDNSResolver *)resolver - didResolveDomainName: (OFString *)domainName - answerRecords: (OFDictionary *)answerRecords - authorityRecords: (OFDictionary *)authorityRecords - additionalRecords: (OFDictionary *)additionalRecords - context: (OFString *)domainToASCII - exception: (id)exception; +@interface XMPPConnection () +- (void)xmpp_tryNextSRVRecord; - (bool)xmpp_parseBuffer: (const void *)buffer length: (size_t)length; -- (bool)xmpp_stream: (OFStream *)stream - didReadIntoBuffer: (char *)buffer - length: (size_t)length - exception: (OFException *)exception; - (void)xmpp_startStream; - (void)xmpp_handleStanza: (OFXMLElement *)element; - (void)xmpp_handleStream: (OFXMLElement *)element; - (void)xmpp_handleTLS: (OFXMLElement *)element; - (void)xmpp_handleSASL: (OFXMLElement *)element; @@ -140,10 +125,11 @@ [_certificateFile release]; [_server release]; [_domain release]; [_resource release]; [_JID release]; + [_nextSRVRecords release]; [_delegates release]; [_callbacks release]; [_authModule release]; [super dealloc]; @@ -269,63 +255,53 @@ _password = nil; [old release]; } -- (void)xmpp_socketDidConnect: (OFTCPSocket *)socket - context: (OFArray *)nextSRVRecords - exception: (id)exception +- (void)socket: (OF_KINDOF(OFTCPSocket *))sock + didConnectToHost: (OFString *)host + port: (uint16_t)port + exception: (id)exception { - char *buffer; - if (exception != nil) { - if (nextSRVRecords != nil) { - [self xmpp_tryNextSRVRecord: nextSRVRecords]; + if ([_nextSRVRecords count] > 0) { + [self xmpp_tryNextSRVRecord]; return; } - [_delegates - broadcastSelector: @selector(connection:didThrowException:) - withObject: self - withObject: exception]; + [_delegates broadcastSelector: @selector(connection: + didThrowException:) + withObject: self + withObject: exception]; return; } [self xmpp_startStream]; - buffer = [self allocMemoryWithSize: BUFFER_LENGTH]; - [_socket asyncReadIntoBuffer: buffer - length: BUFFER_LENGTH - target: self - selector: @selector(xmpp_stream:didReadIntoBuffer: - length:exception:) - context: nil]; -} - -- (void)xmpp_tryNextSRVRecord: (OFArray *)SRVRecords -{ - OFSRVDNSResourceRecord *record = [SRVRecords objectAtIndex: 0]; - - SRVRecords = [SRVRecords objectsInRange: - of_range(1, [SRVRecords count] - 1)]; - if ([SRVRecords count] == 0) - SRVRecords = nil; + [_socket asyncReadIntoBuffer: _buffer + length: XMPP_CONNECTION_BUFFER_LENGTH]; +} + +- (void)xmpp_tryNextSRVRecord +{ + OFSRVDNSResourceRecord *record = + [[[_nextSRVRecords objectAtIndex: 0] copy] autorelease]; + + if ([_nextSRVRecords count] == 0) { + [_nextSRVRecords release]; + _nextSRVRecords = nil; + } [_socket asyncConnectToHost: [record target] - port: [record port] - target: self - selector: @selector(xmpp_socketDidConnect: - context:exception:) - context: SRVRecords]; + port: [record port]]; } -- (void)xmpp_resolver: (OFDNSResolver *)resolver +- (void)resolver: (OFDNSResolver *)resolver didResolveDomainName: (OFString *)domainName answerRecords: (OFDictionary *)answerRecords authorityRecords: (OFDictionary *)authorityRecords additionalRecords: (OFDictionary *)additionalRecords - context: (OFString *)domainToASCII exception: (id)exception { OFMutableArray *records = [OFMutableArray array]; if (exception != nil) { @@ -343,21 +319,20 @@ /* TODO: Sort records */ [records makeImmutable]; if ([records count] == 0) { - /* Fall back to A / AAA record. */ - [_socket asyncConnectToHost: domainToASCII - port: _port - target: self - selector: @selector(xmpp_socketDidConnect: - context:exception:) - context: nil]; + /* Fall back to A / AAAA record. */ + [_socket asyncConnectToHost: _domainToASCII + port: _port]; return; } - [self xmpp_tryNextSRVRecord: records]; + [_nextSRVRecords release]; + _nextSRVRecords = nil; + _nextSRVRecords = [records mutableCopy]; + [self xmpp_tryNextSRVRecord]; } - (void)asyncConnect { void *pool = objc_autoreleasePoolPush(); @@ -364,37 +339,30 @@ if (_socket != nil) @throw [OFAlreadyConnectedException exception]; _socket = [[OFTCPSocket alloc] init]; + [_socket setDelegate: self]; if (_server != nil) [_socket asyncConnectToHost: _server - port: _port - target: self - selector: @selector(xmpp_socketDidConnect: - context:exception:) - context: nil]; + port: _port]; else - [[OFThread DNSResolver] - asyncResolveHost: _domainToASCII - target: self - selector: @selector(xmpp_resolver: - didResolveDomainName:answerRecords: - authorityRecords:additionalRecords: - context:exception:) - context: _domainToASCII]; + [[OFThread DNSResolver] asyncResolveHost: _domainToASCII + delegate: self]; objc_autoreleasePoolPop(pool); } - (bool)xmpp_parseBuffer: (const void *)buffer length: (size_t)length { if ([_socket isAtEndOfStream]) { - [_delegates broadcastSelector: @selector(connectionWasClosed:) - withObject: self]; + [_delegates broadcastSelector: @selector(connectionWasClosed: + error:) + withObject: self + withObject: nil]; return false; } @try { [_parser parseBuffer: buffer @@ -420,14 +388,14 @@ _oldParser = nil; _oldElementBuilder = nil; } -- (bool)xmpp_stream: (OFStream *)stream - didReadIntoBuffer: (char *)buffer +- (bool)stream: (OF_KINDOF(OFStream *))stream + didReadIntoBuffer: (void *)buffer length: (size_t)length - exception: (OFException *)exception + exception: (id)exception { if (exception != nil) { [_delegates broadcastSelector: @selector(connection: didThrowException:) withObject: self @@ -454,18 +422,12 @@ [_oldElementBuilder release]; _oldParser = nil; _oldElementBuilder = nil; - [_socket asyncReadIntoBuffer: buffer - length: BUFFER_LENGTH - target: self - selector: @selector(xmpp_stream: - didReadIntoBuffer:length: - exception:) - context: nil]; - + [_socket asyncReadIntoBuffer: _buffer + length: XMPP_CONNECTION_BUFFER_LENGTH]; return false; } return true; } @@ -584,11 +546,11 @@ } - (void)parser: (OFXMLParser *)parser didStartElement: (OFString *)name prefix: (OFString *)prefix - namespace: (OFString *)NS + namespace: (OFString *)namespace attributes: (OFArray *)attributes { if (![name isEqual: @"stream"]) { // No dedicated stream error for this, may not even be XMPP [self close]; @@ -600,11 +562,11 @@ [self xmpp_sendStreamError: @"bad-namespace-prefix" text: nil]; return; } - if (![NS isEqual: XMPP_NS_STREAM]) { + if (![namespace isEqual: XMPP_NS_STREAM]) { [self xmpp_sendStreamError: @"invalid-namespace" text: nil]; return; } @@ -753,88 +715,19 @@ } if ([[element name] isEqual: @"error"]) { OFString *condition, *reason; [self close]; - [_socket close]; // Remote has already closed his stream - - if ([element elementForName: @"bad-format" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"bad-format"; - else if ([element elementForName: @"bad-namespace-prefix" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"bad-namespace-prefix"; - else if ([element elementForName: @"conflict" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"conflict"; - else if ([element elementForName: @"connection-timeout" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"connection-timeout"; - else if ([element elementForName: @"host-gone" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"host-gone"; - else if ([element elementForName: @"host-unknown" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"host-unknown"; - else if ([element elementForName: @"improper-addressing" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"improper-addressing"; - else if ([element elementForName: @"internal-server-error" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"internal-server-error"; - else if ([element elementForName: @"invalid-from" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"invalid-from"; - else if ([element elementForName: @"invalid-namespace" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"invalid-namespace"; - else if ([element elementForName: @"invalid-xml" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"invalid-xml"; - else if ([element elementForName: @"not-authorized" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"not-authorized"; - else if ([element elementForName: @"not-well-formed" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"not-well-formed"; - else if ([element elementForName: @"policy-violation" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"policy-violation"; - else if ([element elementForName: @"remote-connection-failed" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"remote-connection-failed"; - else if ([element elementForName: @"reset" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"reset"; - else if ([element elementForName: @"resource-constraint" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"resource-constraint"; - else if ([element elementForName: @"restricted-xml" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"restricted-xml"; - else if ([element elementForName: @"see-other-host" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"see-other-host"; - else if ([element elementForName: @"system-shutdown" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"system-shutdown"; - else if ([element elementForName: @"undefined-condition" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"undefined-condition"; - else if ([element elementForName: @"unsupported-encoding" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"unsupported-encoding"; - else if ([element elementForName: @"unsupported-feature" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"unsupported-feature"; - else if ([element elementForName: @"unsupported-stanza-type" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"unsupported-stanza-type"; - else if ([element elementForName: @"unsupported-version" - namespace: XMPP_NS_XMPP_STREAM]) - condition = @"unsupported-version"; - else + + [_delegates broadcastSelector: @selector(connectionWasClosed:) + withObject: self + withObject: element]; + + condition = [[[element elementsForNamespace: + XMPP_NS_XMPP_STREAM] firstObject] name]; + + if (condition == nil) condition = @"undefined"; reason = [[element elementForName: @"text" namespace: XMPP_NS_XMPP_STREAM] stringValue]; @@ -868,10 +761,11 @@ [newSock setPrivateKeyPassphrase: _privateKeyPassphrase]; #endif [newSock startTLSWithExpectedHost: nil]; [_socket release]; _socket = newSock; + [_socket setDelegate: self]; _encrypted = true; [_delegates broadcastSelector: @selector( connectionDidUpgradeToTLS:) Index: tests/test.m ================================================================== --- tests/test.m +++ tests/test.m @@ -267,9 +267,10 @@ { @throw e; } - (void)connectionWasClosed: (XMPPConnection *)conn + error: (OFXMLElement *)error { - of_log(@"Connection was closed!"); + of_log(@"Connection was closed: %@", error); } @end