Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -46,53 +46,10 @@ AC_PATH_TOOL(AR, ar) AC_PROG_RANLIB AC_SUBST(OBJXMPP_STATIC_LIB, "libobjxmpp.a") ]) -# This is an adapted version of what glib does for res_query -# It should recognize the correct library on (at least) Linux, -# NetBSD, FreeBSD, Mac OS X and Haiku -AC_MSG_CHECKING([for res_nsearch]) -AC_TRY_LINK([#include - #include - #include - #include ], - [res_nsearch(&_res, "test", 0, 0, (void *)0, 0);], - [AC_MSG_RESULT([yes])], - [save_libs="$LIBS" - LIBS="$LIBS -lresolv" - AC_TRY_LINK([#include - #include - #include - #include ], - [res_nsearch(&_res, "test", 0, 0, (void *)0, 0);], - [AC_MSG_RESULT([in -lresolv])], - [LIBS="$save_libs -lnetwork" - AC_TRY_LINK([#include - #include - #include - #include ], - [res_nsearch(&_res, "test", 0, 0, (void *)0, 0);], - [AC_MSG_RESULT([in -lnetwork])], - [LIBS="$save_libs -lbind" - AC_TRY_LINK([#include ], - [res_nsearch(&_res, "test", 0, 0, (void *)0, 0);], - [AC_MSG_RESULT([in -lbind])], - [AC_MSG_ERROR(not found)])])])]) - -AC_MSG_CHECKING([for res_ndestroy]) -AC_TRY_LINK([ - #include -], [ - res_ndestroy(&_res) -], [ - AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_RES_NDESTROY, 1, [Whether we have res_ndestroy]) -], [ - AC_MSG_RESULT(no) -]) - AC_CHECK_LIB(objopenssl, main, [ AC_SUBST(OBJOPENSSL_LIBS, "-lobjopenssl -lcrypto") AC_SUBST(OBJOPENSSL_FRAMEWORK_LIBS, "-framework ObjOpenSSL -lcrypto") ], [ AC_MSG_ERROR(You need ObjOpenSSL installed!) Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -24,11 +24,10 @@ XMPPPLAINAuth.m \ XMPPPresence.m \ XMPPRoster.m \ XMPPRosterItem.m \ XMPPSCRAMAuth.m \ - XMPPSRVLookup.m \ XMPPStanza.m \ XMPPStreamManagement.m \ XMPPXMLElementBuilder.m INCLUDES = ${SRCS:.m=.h} \ Index: src/XMPPConnection.h ================================================================== --- src/XMPPConnection.h +++ src/XMPPConnection.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010, 2011, 2012, 2013, 2016 + * Copyright (c) 2010, 2011, 2012, 2013, 2016, 2017, 2018 * Jonathan Schleifer * Copyright (c) 2011, 2012, Florian Zeitz * * https://heap.zone/objxmpp/ * @@ -114,13 +114,10 @@ - (void)connectionWasClosed: (XMPPConnection *)connection; /*! * @brief This callback is called when the connection threw an exception. * - * This is only called for connections on which - * @ref XMPPConnection::handleConnection has been called. - * * @param connection The connection which threw an exception * @param exception The exception the connection threw */ - (void)connection: (XMPPConnection *)connection didThrowException: (OFException *)exception; @@ -270,15 +267,10 @@ * * @param delegate The delegate to remove */ - (void)removeDelegate: (id )delegate; -/*! - * @brief Connects to the XMPP service. - */ -- (void)connect; - /*! * @brief Closes the stream to the XMPP service */ - (void)close; @@ -293,19 +285,13 @@ */ - (bool)checkCertificateAndGetReason: (OFString *__autoreleasing _Nonnull *_Nullable)reason; /*! - * @brief Adds the connection to the run loop. - */ -- (void)handleConnection; - -/*! - * @brief Asynchronously connects to the server and adds the connection to the - * run loop. - */ -- (void)asyncConnectAndHandle; + * @brief Asynchronously connects to the server. + */ +- (void)asyncConnect; /*! * @brief Parses the specified buffer. * * This is useful for handling multiple connections at once. Index: src/XMPPConnection.m ================================================================== --- src/XMPPConnection.m +++ src/XMPPConnection.m @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010, 2011, 2012, 2013, 2015, 2016 + * Copyright (c) 2010, 2011, 2012, 2013, 2015, 2016, 2017, 2018 * Jonathan Schleifer * Copyright (c) 2011, 2012, Florian Zeitz * * https://heap.zone/objxmpp/ * @@ -39,11 +39,10 @@ #import #import "XMPPConnection.h" #import "XMPPCallback.h" -#import "XMPPSRVLookup.h" #import "XMPPEXTERNALAuth.h" #import "XMPPSCRAMAuth.h" #import "XMPPPLAINAuth.h" #import "XMPPStanza.h" #import "XMPPJID.h" @@ -82,70 +81,12 @@ IQ: (XMPPIQ *)IQ; - (OFString *)XMPP_IDNAToASCII: (OFString *)domain; - (XMPPMulticastDelegate *)XMPP_delegates; @end -@interface XMPPConnection_ConnectThread: OFThread -{ - OFThread *_sourceThread; - XMPPConnection *_connection; -} - -- initWithSourceThread: (OFThread *)sourceThread - connection: (XMPPConnection *)connection; -@end - OF_ASSUME_NONNULL_END -@implementation XMPPConnection_ConnectThread -- initWithSourceThread: (OFThread *)sourceThread - connection: (XMPPConnection *)connection -{ - self = [super init]; - - @try { - _sourceThread = [sourceThread retain]; - _connection = [connection retain]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (void)dealloc -{ - [_sourceThread release]; - [_connection release]; - - [super dealloc]; -} - -- (void)didConnect -{ - [self join]; - - [_connection handleConnection]; -} - -- (id)main -{ - void *pool = objc_autoreleasePoolPush(); - - [_connection connect]; - - [self performSelector: @selector(didConnect) - onThread: _sourceThread - waitUntilDone: false]; - - objc_autoreleasePoolPop(pool); - - return nil; -} -@end - @implementation XMPPConnection @synthesize username = _username, resource = _resource, server = _server; @synthesize domain = _domain, password = _password, language = _language; @synthesize privateKeyFile = _privateKeyFile; @synthesize certificateFile = _certificateFile, socket = _socket; @@ -314,76 +255,122 @@ _password = nil; [old release]; } -- (void)connect +- (void)XMPP_socketDidConnect: (OFTCPSocket *)socket + context: (OFArray *)nextSRVRecords + exception: (id)exception +{ + char *buffer; + + if (exception != nil) { + if (nextSRVRecords != nil) { + [self XMPP_tryNextSRVRecord: nextSRVRecords]; + return; + } + + [_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 asyncConnectToHost: [record target] + port: [record port] + target: self + selector: @selector(XMPP_socketDidConnect: + context:exception:) + context: SRVRecords]; +} + +- (void)XMPP_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) { + [_delegates + broadcastSelector: @selector(connection:didThrowException:) + withObject: self + withObject: exception]; + return; + } + + for (OF_KINDOF(OFDNSResourceRecord *) record in + [answerRecords objectForKey: domainName]) + if ([record isKindOfClass: [OFSRVDNSResourceRecord class]]) + [records addObject: record]; + + /* 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]; + return; + } + + [self XMPP_tryNextSRVRecord: records]; +} + +- (void)asyncConnect { void *pool = objc_autoreleasePoolPush(); - XMPPSRVEntry *candidate = nil; - XMPPSRVLookup *SRVLookup = nil; - OFEnumerator *enumerator; if (_socket != nil) @throw [OFAlreadyConnectedException exception]; _socket = [[OFTCPSocket alloc] init]; - if (_server) - [_socket connectToHost: _server - port: _port]; - else { - @try { - SRVLookup = [XMPPSRVLookup - lookupWithDomain: _domainToASCII]; - } @catch (id e) { - } - - enumerator = [SRVLookup objectEnumerator]; - - /* Iterate over SRV records, if any */ - if ((candidate = [enumerator nextObject]) != nil) { - do { - @try { - [_socket - connectToHost: [candidate target] - port: [candidate port]]; - break; - } @catch (OFResolveHostFailedException *e) { - } @catch (OFConnectionFailedException *e) { - } - } while ((candidate = [enumerator nextObject]) != nil); - } else - /* No SRV records -> fall back to A / AAAA record */ - [_socket connectToHost: _domainToASCII - port: _port]; - } - - [self XMPP_startStream]; - - objc_autoreleasePoolPop(pool); -} - -- (void)handleConnection -{ - char *buffer = [self allocMemoryWithSize: BUFFER_LENGTH]; - - [_socket asyncReadIntoBuffer: buffer - length: BUFFER_LENGTH + if (_server != nil) + [_socket asyncConnectToHost: _server + port: _port + target: self + selector: @selector(XMPP_socketDidConnect: + context:exception:) + context: nil]; + else + [[OFThread DNSResolver] + asyncResolveHost: _domainToASCII target: self - selector: @selector(stream:didReadIntoBuffer:length: - exception:) - context: nil]; -} - -- (void)asyncConnectAndHandle -{ - void *pool = objc_autoreleasePoolPush(); - - [[[[XMPPConnection_ConnectThread alloc] - initWithSourceThread: [OFThread currentThread] - connection: self] autorelease] start]; + selector: @selector(XMPP_resolver: + didResolveDomainName:answerRecords: + authorityRecords:additionalRecords: + context:exception:) + context: _domainToASCII]; objc_autoreleasePoolPop(pool); } - (bool)XMPP_parseBuffer: (const void *)buffer @@ -419,11 +406,11 @@ _oldParser = nil; _oldElementBuilder = nil; } -- (bool)stream: (OFStream *)stream +- (bool)XMPP_stream: (OFStream *)stream didReadIntoBuffer: (char *)buffer length: (size_t)length exception: (OFException *)exception { if (exception != nil) { @@ -456,11 +443,11 @@ _oldElementBuilder = nil; [_socket asyncReadIntoBuffer: buffer length: BUFFER_LENGTH target: self - selector: @selector(stream: + selector: @selector(XMPP_stream: didReadIntoBuffer:length: exception:) context: nil]; return false; @@ -925,12 +912,11 @@ return; } if ([[element name] isEqual: @"failure"]) { - of_log(@"Auth failed!"); - // FIXME: Do more parsing/handling + /* FIXME: Do more parsing/handling */ @throw [XMPPAuthFailedException exceptionWithConnection: self reason: [element XMLString]]; } DELETED src/XMPPSRVLookup.h Index: src/XMPPSRVLookup.h ================================================================== --- src/XMPPSRVLookup.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2011, 2012, Florian Zeitz - * Copyright (c) 2011, 2012, 2013, 2016, Jonathan Schleifer - * - * https://heap.zone/objxmpp/ - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice is present in all copies. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include - -#import - -OF_ASSUME_NONNULL_BEGIN - -@interface XMPPSRVEntry: OFObject -{ - uint16_t _priority; - uint16_t _weight; - uint32_t _accumulatedWeight; - uint16_t _port; - OFString *_target; -} - -@property (readonly, nonatomic) uint16_t priority; -@property (readonly, nonatomic) uint16_t weight; -@property (nonatomic) uint32_t accumulatedWeight; -@property (readonly, nonatomic) uint16_t port; -@property (readonly, nonatomic) OFString *target; - -+ (instancetype)entryWithPriority: (uint16_t)priority - weight: (uint16_t)weight - port: (uint16_t)port - target: (OFString *)target; -+ (instancetype)entryWithResourceRecord: (ns_rr)resourceRecord - handle: (ns_msg)handle; -- init OF_UNAVAILABLE; -- initWithPriority: (uint16_t)priority - weight: (uint16_t)weight - port: (uint16_t)port - target: (OFString *)target; -- initWithResourceRecord: (ns_rr)resourceRecord - handle: (ns_msg)handle; -@end - -@interface XMPPSRVLookup: OFObject -{ - OFString *_domain; - struct __res_state _resState; - OFList *_list; -} - -@property (readonly, nonatomic) OFString *domain; - -+ (instancetype)lookupWithDomain: (OFString *)domain; -- init OF_UNAVAILABLE; -- initWithDomain: (OFString *)domain; -@end - -@interface XMPPSRVEnumerator: OFEnumerator -{ - OFList *_list; - of_list_object_t *_listIter; - OFList *_subListCopy; - bool _done; -} - -- init OF_UNAVAILABLE; -- initWithList: (OFList *)list; -@end - -OF_ASSUME_NONNULL_END DELETED src/XMPPSRVLookup.m Index: src/XMPPSRVLookup.m ================================================================== --- src/XMPPSRVLookup.m +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 - * Jonathan Schleifer - * Copyright (c) 2011, 2012, 2013, 2014, Florian Zeitz - * - * https://heap.zone/objxmpp/ - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice is present in all copies. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include - -#include - -#include -#include -#include -#include - -#import "XMPPSRVLookup.h" - -#import - -OF_ASSUME_NONNULL_BEGIN - -@interface XMPPSRVLookup () -- (void)XMPP_lookup; -- (void)XMPP_addEntry: (XMPPSRVEntry *)item; -@end - -OF_ASSUME_NONNULL_END - -@implementation XMPPSRVEntry -@synthesize priority = _priority, weight = _weight; -@synthesize accumulatedWeight = _accumulatedWeight, port = _port; -@synthesize target = _target; - -+ (instancetype)entryWithPriority: (uint16_t)priority - weight: (uint16_t)weight - port: (uint16_t)port - target: (OFString *)target -{ - return [[[self alloc] initWithPriority: priority - weight: weight - port: port - target: target] autorelease]; -} - -+ (instancetype)entryWithResourceRecord: (ns_rr)resourceRecord - handle: (ns_msg)handle -{ - return [[[self alloc] initWithResourceRecord: resourceRecord - handle: handle] autorelease]; -} - -- init -{ - OF_INVALID_INIT_METHOD -} - -- initWithPriority: (uint16_t)priority - weight: (uint16_t)weight - port: (uint16_t)port - target: (OFString *)target -{ - self = [super init]; - - @try { - _priority = priority; - _weight = weight; - _port = port; - _target = [target copy]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- initWithResourceRecord: (ns_rr)resourceRecord - handle: (ns_msg)handle -{ - self = [super init]; - - @try { - const uint16_t *rdata; - char buffer[NS_MAXDNAME]; - - rdata = (const uint16_t *)(void *)ns_rr_rdata(resourceRecord); - _priority = ntohs(rdata[0]); - _weight = ntohs(rdata[1]); - _port = ntohs(rdata[2]); - - if (dn_expand(ns_msg_base(handle), ns_msg_end(handle), - (uint8_t *)&rdata[3], buffer, NS_MAXDNAME) < 1) - @throw [OFInitializationFailedException - exceptionWithClass: [self class]]; - - _target = [[OFString alloc] - initWithCString: buffer - encoding: [OFLocale encoding]]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (void)dealloc -{ - [_target release]; - - [super dealloc]; -} - -- (OFString *)description -{ - return [OFString stringWithFormat: - @"<%@ priority: %" PRIu16 @", weight: %" PRIu16 @", target: %@:%" - PRIu16 @">", [self class], _priority, _weight, _target, _port]; -} -@end - -@implementation XMPPSRVLookup -@synthesize domain = _domain; - -+ (instancetype)lookupWithDomain: (OFString *)domain -{ - return [[[self alloc] initWithDomain: domain] autorelease]; -} - -- init -{ - OF_INVALID_INIT_METHOD -} - -- initWithDomain: (OFString *)domain -{ - self = [super init]; - - @try { - _list = [[OFList alloc] init]; - _domain = [domain copy]; - - [self XMPP_lookup]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (void)dealloc -{ - [_list release]; - [_domain release]; - - [super dealloc]; -} - -- (void)XMPP_lookup -{ - void *pool = objc_autoreleasePoolPush(); - unsigned char *answer = NULL; - size_t pageSize = [OFSystemInfo pageSize]; - OFString *request; - - request = [OFString stringWithFormat: @"_xmpp-client._tcp.%@", _domain]; - - @try { - int answerLen, resourceRecordCount, i; - ns_rr resourceRecord; - ns_msg handle; - - if (res_ninit(&_resState)) - @throw [OFResolveHostFailedException - exceptionWithHost: _domain - recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN - recordType: OF_DNS_RESOURCE_RECORD_TYPE_SRV - error: OF_DNS_RESOLVER_ERROR_UNKNOWN]; - - answer = [self allocMemoryWithSize: pageSize]; - answerLen = res_nsearch(&_resState, - [request cStringWithEncoding: [OFLocale encoding]], - ns_c_in, ns_t_srv, answer, (int)pageSize); - - if ((answerLen == -1) && ((h_errno == HOST_NOT_FOUND) || - (h_errno == NO_DATA))) - return; - - if (answerLen < 1 || answerLen > pageSize) - @throw [OFResolveHostFailedException - exceptionWithHost: _domain - recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN - recordType: OF_DNS_RESOURCE_RECORD_TYPE_SRV - error: OF_DNS_RESOLVER_ERROR_UNKNOWN]; - - if (ns_initparse(answer, answerLen, &handle)) - @throw [OFResolveHostFailedException - exceptionWithHost: _domain - recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN - recordType: OF_DNS_RESOURCE_RECORD_TYPE_SRV - error: OF_DNS_RESOLVER_ERROR_UNKNOWN]; - - resourceRecordCount = ns_msg_count(handle, ns_s_an); - for (i = 0; i < resourceRecordCount; i++) { - if (ns_parserr(&handle, ns_s_an, i, &resourceRecord)) - continue; - - if (ns_rr_type(resourceRecord) != ns_t_srv || - ns_rr_class(resourceRecord) != ns_c_in) - continue; - - [self XMPP_addEntry: [XMPPSRVEntry - entryWithResourceRecord: resourceRecord - handle: handle]]; - } - } @finally { - [self freeMemory: answer]; -#ifdef HAVE_RES_NDESTROY - res_ndestroy(&_resState); -#endif - } - - objc_autoreleasePoolPop(pool); -} - -- (void)XMPP_addEntry: (XMPPSRVEntry *)entry -{ - void *pool = objc_autoreleasePoolPush(); - OFList *subList; - of_list_object_t *iter; - - /* Look if there already is a list with the priority */ - for (iter = [_list firstListObject]; iter != NULL; iter = iter->next) { - XMPPSRVEntry *first = [iter->object firstObject]; - - if ([first priority] == [entry priority]) { - /* - * RFC 2782 says those with weight 0 should be at the - * beginning of the list. - */ - if ([entry weight] > 0) - [iter->object appendObject: entry]; - else - [iter->object prependObject: entry]; - - return; - } - - /* We can't have one if the priority is already bigger */ - if ([first priority] > [entry priority]) - break; - } - - subList = [OFList list]; - [subList appendObject: entry]; - - if (iter != NULL) - [_list insertObject: subList - beforeListObject: iter]; - else - [_list appendObject: subList]; - - objc_autoreleasePoolPop(pool); -} - -- (OFEnumerator *)objectEnumerator -{ - return [[[XMPPSRVEnumerator alloc] initWithList: _list] autorelease]; -} -@end - -@implementation XMPPSRVEnumerator -- init -{ - OF_INVALID_INIT_METHOD -} - -- initWithList: (OFList *)list -{ - self = [super init]; - - @try { - _list = [list copy]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (id)nextObject -{ - XMPPSRVEntry *ret = nil; - of_list_object_t *iter; - uint32_t totalWeight = 0; - - if (_done) - return nil; - - if (_listIter == NULL) - _listIter = [_list firstListObject]; - - if (_listIter == NULL) - return nil; - - if (_subListCopy == nil) - _subListCopy = [_listIter->object copy]; - - for (iter = [_subListCopy firstListObject]; iter != NULL; - iter = iter->next) { - totalWeight += [iter->object weight]; - [iter->object setAccumulatedWeight: totalWeight]; - } - - if ([_subListCopy count] > 0) { - uint32_t randomWeight; - - RAND_pseudo_bytes((uint8_t *)&randomWeight, sizeof(uint32_t)); - randomWeight %= (totalWeight + 1); - - for (iter = [_subListCopy firstListObject]; iter != NULL; - iter = iter->next) { - if ([iter->object accumulatedWeight] >= randomWeight) { - ret = [[iter->object retain] autorelease]; - - [_subListCopy removeListObject: iter]; - - break; - } - } - } - - if ([_subListCopy count] == 0) { - [_subListCopy release]; - _subListCopy = nil; - - _listIter = _listIter->next; - - if (_listIter == NULL) - _done = true; - } - - return ret; -} - -- (void)reset -{ - _listIter = NULL; - [_subListCopy release]; - _subListCopy = nil; - _done = false; -} -@end Index: tests/test.m ================================================================== --- tests/test.m +++ tests/test.m @@ -124,11 +124,11 @@ [conn setDomain: [arguments objectAtIndex: 0]]; [conn setUsername: [arguments objectAtIndex: 1]]; [conn setPassword: [arguments objectAtIndex: 2]]; [conn setResource: @"ObjXMPP"]; - [conn asyncConnectAndHandle]; + [conn asyncConnect]; } - (void)connection: (XMPPConnection *)conn didReceiveElement: (OFXMLElement *)element {