Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -8,10 +8,13 @@ SRCS = XMPPAuthenticator.m \ XMPPCallback.m \ XMPPConnection.m \ XMPPContact.m \ XMPPContactManager.m \ + XMPPDiscoEntity.m \ + XMPPDiscoIdentity.m \ + XMPPDiscoNode.m \ XMPPExceptions.m \ XMPPEXTERNALAuth.m \ XMPPIQ.m \ XMPPJID.m \ XMPPFileStorage.m \ ADDED src/XMPPDiscoEntity.h Index: src/XMPPDiscoEntity.h ================================================================== --- src/XMPPDiscoEntity.h +++ src/XMPPDiscoEntity.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2013, Florian Zeitz + * + * https://webkeks.org/git/?p=objxmpp.git + * + * 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. + */ + +#import + +#import "XMPPConnection.h" +#import "XMPPDiscoNode.h" + +@class XMPPJID; + +/** + * \brief A class representing an entity responding to Service Discovery + * queries + */ +@interface XMPPDiscoEntity: XMPPDiscoNode +{ + OFMutableDictionary *_discoNodes; + XMPPConnection *_connection; +} +#ifdef OF_HAVE_PROPERTIES +/** + * \brief The XMPPDiscoNodes this entity provides Services Discovery + * responses for + * + * This usually contains at least all immediate child nodes, but may contain + * any number of nodes nested more deeply. + */ +@property (readonly) OFDictionary *discoNodes; +#endif + +/** + * \brief Creates a new autoreleased XMPPDiscoEntity with the specified + * connection. + * + * \param connection The XMPPConnection to serve responses on. + * This must already be bound to a resource) + * \return A new autoreleased XMPPDiscoEntity + */ ++ discoEntityWithConnection: (XMPPConnection*)connection; + +/** + * \brief Initializes an already allocated XMPPDiscoEntity with the specified + * connection. + * + * \param connection The XMPPConnection to serve responses on. + * This must already be bound to a resource) + * \return An initialized XMPPDiscoEntity + */ +- initWithConnection: (XMPPConnection*)connection; + +/** + * \brief Adds a XMPPDiscoNode to provide responses for. + * + * \param node The XMPPDiscoNode to provide responses for + */ +- (void)addDiscoNode: (XMPPDiscoNode*)node; + +- (OFDictionary*)discoNodes; +@end ADDED src/XMPPDiscoEntity.m Index: src/XMPPDiscoEntity.m ================================================================== --- src/XMPPDiscoEntity.m +++ src/XMPPDiscoEntity.m @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2013, Florian Zeitz + * + * https://webkeks.org/git/?p=objxmpp.git + * + * 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. + */ + +#import "XMPPDiscoEntity.h" +#import "XMPPIQ.h" +#import "namespaces.h" + +@implementation XMPPDiscoEntity ++ discoEntityWithConnection: (XMPPConnection*)connection +{ + return [[[self alloc] initWithConnection: connection] autorelease]; +} + +- initWithConnection: (XMPPConnection*)connection +{ + self = [super initWithJID: [connection JID] + node: nil]; + + @try { + _discoNodes = [OFMutableDictionary new]; + _connection = connection; + + [_connection addDelegate: self]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_connection removeDelegate: self]; + [_discoNodes release]; + + [super dealloc]; +} + +- (OFDictionary*)discoNodes; +{ + OF_GETTER(_discoNodes, YES); +} + +- (void)addDiscoNode: (XMPPDiscoNode*)node +{ + [_discoNodes setObject: node + forKey: [node node]]; +} + +- (BOOL)connection: (XMPPConnection*)connection + didReceiveIQ: (XMPPIQ*)IQ +{ + of_log(@"Called connection:didReceiveIQ:... %@ %@", [IQ to], _JID); + if (![[IQ to] isEqual: _JID]) + return NO; + + of_log(@"...that is for us"); + + OFXMLElement *query = [IQ elementForName: @"query" + namespace: XMPP_NS_DISCO_ITEMS]; + + if (query != nil) { + OFString *node = + [[query attributeForName: @"node"] stringValue]; + if (node == nil) + return [self XMPP_handleItemsIQ: IQ + connection: connection]; + + XMPPDiscoNode *responder = [_discoNodes objectForKey: node]; + if (responder != nil) + return [responder XMPP_handleItemsIQ: IQ + connection: connection]; + + return NO; + } + + query = [IQ elementForName: @"query" + namespace: XMPP_NS_DISCO_INFO]; + + if (query != nil) { + OFString *node = + [[query attributeForName: @"node"] stringValue]; + if (node == nil) + return [self XMPP_handleInfoIQ: IQ + connection: connection]; + + XMPPDiscoNode *responder = [_discoNodes objectForKey: node]; + if (responder != nil) + return [responder XMPP_handleInfoIQ: IQ + connection: connection]; + + return NO; + } + + return NO; +} +@end ADDED src/XMPPDiscoIdentity.h Index: src/XMPPDiscoIdentity.h ================================================================== --- src/XMPPDiscoIdentity.h +++ src/XMPPDiscoIdentity.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2013, Florian Zeitz + * + * https://webkeks.org/git/?p=objxmpp.git + * + * 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. + */ + +#import + +/** + * \brief A class describing a Service Discovery Identity + */ +@interface XMPPDiscoIdentity: OFObject +{ + OFString *_category; + OFString *_name; + OFString *_type; +} +#ifdef OF_HAVE_PROPERTIES +/// \brief The category of the identity +@property (readonly) OFString *category; +/// \brief The name of the identity, might be unset +@property (readonly) OFString *name; +/// \brief The type of the identity +@property (readonly) OFString *type; +#endif + +/** + * \brief Creates a new autoreleased XMPPDiscoIdentity with the specified + * category, type and name. + * + * \param category The category of the identity + * \param type The type of the identity + * \param name The name of the identity + * \return A new autoreleased XMPPDiscoIdentity + */ ++ identityWithCategory: (OFString*)category + type: (OFString*)type + name: (OFString*)name; + +/** + * \brief Creates a new autoreleased XMPPDiscoIdentity with the specified + * category and type. + * + * \param category The category of the identity + * \param type The type of the identity + * \return A new autoreleased XMPPDiscoIdentity + */ ++ identityWithCategory: (OFString*)category + type: (OFString*)type; + +/** + * \brief Initializes an already allocated XMPPDiscoIdentity with the specified + * category, type and name. + * + * \param category The category of the identity + * \param type The type of the identity + * \param name The name of the identity + * \return An initialized XMPPDiscoIdentity + */ +- initWithCategory: (OFString*)category + type: (OFString*)type + name: (OFString*)name; + +/** + * \brief Initializes an already allocated XMPPDiscoIdentity with the specified + * category and type. + * + * \param category The category of the identity + * \param type The type of the identity + * \return An initialized XMPPDiscoIdentity + */ +- initWithCategory: (OFString*)category + type: (OFString*)type; + +- (OFString*)category; +- (OFString*)name; +- (OFString*)type; +@end ADDED src/XMPPDiscoIdentity.m Index: src/XMPPDiscoIdentity.m ================================================================== --- src/XMPPDiscoIdentity.m +++ src/XMPPDiscoIdentity.m @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2013, Florian Zeitz + * + * https://webkeks.org/git/?p=objxmpp.git + * + * 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. + */ + +#import "XMPPDiscoIdentity.h" + +@implementation XMPPDiscoIdentity ++ identityWithCategory: (OFString*)category + type: (OFString*)type + name: (OFString*)name +{ + return [[[self alloc] initWithCategory: category + type: type + name: name] autorelease]; +} + ++ identityWithCategory: (OFString*)category + type: (OFString*)type +{ + return [[[self alloc] initWithCategory: category + type: type] autorelease]; +} + +- initWithCategory: (OFString*)category + type: (OFString*)type + name: (OFString*)name +{ + self = [super init]; + + @try { + if (category == nil || type == nil) + @throw [OFInvalidArgumentException + exceptionWithClass: [self class] + selector: _cmd]; + + _category = [category copy]; + _name = [name copy]; + _type = [type copy]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- initWithCategory: (OFString*)category + type: (OFString*)type +{ + return [self initWithCategory: category + type: type + name: nil]; +} + +- init +{ + @try { + [self doesNotRecognizeSelector: _cmd]; + } @catch (id e) { + [self release]; + @throw e; + } + + abort(); +} + +- (void)dealloc +{ + [_category release]; + [_name release]; + [_type release]; + + [super dealloc]; +} + +- (OFString*)category +{ + OF_GETTER(_category, YES); +} + +- (OFString*)name +{ + OF_GETTER(_name, YES); +} + +- (OFString*)type +{ + OF_GETTER(_type, YES); +} + +- (bool)isEqual: (id)object +{ + XMPPDiscoIdentity *identity; + + if (object == self) + return YES; + + if (![object isKindOfClass: [XMPPDiscoIdentity class]]) + return NO; + + identity = object; + + if ([_category isEqual: identity->_category] && + (_name == identity->_name || [_name isEqual: identity->_name]) && + [_type isEqual: identity->_type]) + return YES; + + return NO; +} + +- (uint32_t) hash +{ + uint32_t hash; + + OF_HASH_INIT(hash); + + OF_HASH_ADD_HASH(hash, [_category hash]); + OF_HASH_ADD_HASH(hash, [_type hash]); + OF_HASH_ADD_HASH(hash, [_name hash]); + + OF_HASH_FINALIZE(hash); + + return hash; +} + +- (of_comparison_result_t)compare: (id )object +{ + XMPPDiscoIdentity *identity; + of_comparison_result_t categoryResult; + of_comparison_result_t typeResult; + + if (object == self) + return OF_ORDERED_SAME; + + if (![object isKindOfClass: [XMPPDiscoIdentity class]]) + @throw [OFInvalidArgumentException + exceptionWithClass: [self class] + selector: _cmd]; + + identity = (XMPPDiscoIdentity*)object; + + categoryResult = [_category compare: identity->_category]; + if (categoryResult != OF_ORDERED_SAME) + return categoryResult; + + typeResult = [_type compare: identity->_type]; + if (typeResult != OF_ORDERED_SAME) + return typeResult; + + return [_name compare: identity->_name]; +} +@end ADDED src/XMPPDiscoNode.h Index: src/XMPPDiscoNode.h ================================================================== --- src/XMPPDiscoNode.h +++ src/XMPPDiscoNode.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2013, Florian Zeitz + * + * https://webkeks.org/git/?p=objxmpp.git + * + * 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. + */ + +#import + +@class XMPPDiscoIdentity; +@class XMPPJID; + +/** + * \brief A class describing a Service Discovery Node + */ +@interface XMPPDiscoNode: OFObject +{ + XMPPJID *_JID; + OFString *_node; + OFString *_name; + OFSortedList *_identities; + OFSortedList *_features; + OFMutableDictionary *_childNodes; +} +#ifdef OF_HAVE_PROPERTIES +/// \brief The JID this node lives on +@property (readonly) XMPPJID *JID; +/// \brief The node's opaque name of the node +@property (readonly) OFString *node; +/// \brief The node's human friendly name (may be unspecified) +@property (readonly) OFString *name; +/// \brief The node's list of identities +@property (readonly) OFSortedList *identities; +/// \brief The node's list of features +@property (readonly) OFSortedList *features; +/// \brief The node's children +@property (readonly) OFDictionary *childNodes; +#endif + +/** + * \brief Creates a new autoreleased XMPPDiscoNode with the specified + * JID and node + * + * \param JID The JID this node lives on + * \param node The node's opaque name + * \return A new autoreleased XMPPDiscoNode + */ ++ discoNodeWithJID: (XMPPJID*)JID + node: (OFString*)node; + +/** + * \brief Creates a new autoreleased XMPPDiscoNode with the specified + * JID, node and name + * + * \param JID The JID this node lives on + * \param node The node's opaque name + * \param name The node's human friendly name + * \return A new autoreleased XMPPDiscoNode + */ ++ discoNodeWithJID: (XMPPJID*)JID + node: (OFString*)node + name: (OFString*)name; + +/** + * \brief Initializes an already allocated XMPPDiscoNode with the specified + * JID and node + * + * \param JID The JID this node lives on + * \param node The node's opaque name + * \return An initialized XMPPDiscoNode + */ +- initWithJID: (XMPPJID*)JID + node: (OFString*)node; + +/** + * \brief Initializes an already allocated XMPPDiscoNode with the specified + * JID, node and name + * + * \param JID The JID this node lives on + * \param node The node's opaque name + * \param name The node's human friendly name + * \return An initialized XMPPDiscoNode + */ +- initWithJID: (XMPPJID*)JID + node: (OFString*)node + name: (OFString*)name; + + /** + * \brief Adds an XMPPDiscoIdentity to the node + * + * \param identity The XMPPDiscoIdentity to add + */ +- (void)addIdentity: (XMPPDiscoIdentity*)identity; + + /** + * \brief Adds a feature to the node + * + * \param feature The feature to add + */ +- (void)addFeature: (OFString*)feature; + + /** + * \brief Adds a XMPPDiscoNode as child of the node + * + * \param node The XMPPDiscoNode to add as child + */ +- (void)addChildNode: (XMPPDiscoNode*)node; + +- (XMPPJID*)JID; +- (OFString*)node; +- (OFSortedList*)identities; +- (OFSortedList*)features; +- (OFDictionary*)childNodes; + +- (BOOL)XMPP_handleItemsIQ: (XMPPIQ*)IQ + connection: (XMPPConnection*)connection; +- (BOOL)XMPP_handleInfoIQ: (XMPPIQ*)IQ + connection: (XMPPConnection*)connection; +@end ADDED src/XMPPDiscoNode.m Index: src/XMPPDiscoNode.m ================================================================== --- src/XMPPDiscoNode.m +++ src/XMPPDiscoNode.m @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2013, Florian Zeitz + * + * https://webkeks.org/git/?p=objxmpp.git + * + * 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. + */ + +#import "XMPPConnection.h" +#import "XMPPIQ.h" +#import "XMPPJID.h" +#import "XMPPDiscoNode.h" +#import "XMPPDiscoIdentity.h" +#import "namespaces.h" + +@implementation XMPPDiscoNode ++ discoNodeWithJID: (XMPPJID*)JID + node: (OFString*)node; +{ + return [[[self alloc] initWithJID: JID + node: node] autorelease]; +} + + ++ discoNodeWithJID: (XMPPJID*)JID + node: (OFString*)node + name: (OFString*)name +{ + return [[[self alloc] initWithJID: JID + node: node + name: name] autorelease]; +} + +- initWithJID: (XMPPJID*)JID + node: (OFString*)node +{ + return [self initWithJID: JID + node: node + name: nil]; +} + +- initWithJID: (XMPPJID*)JID + node: (OFString*)node + name: (OFString*)name +{ + self = [super init]; + + @try { + if (JID == nil) + @throw [OFInvalidArgumentException + exceptionWithClass: [self class] + selector: _cmd]; + + _JID = [JID copy]; + _node= [node copy]; + _name = [name copy]; + _identities = [OFSortedList new]; + _features = [OFSortedList new]; + _childNodes = [OFMutableDictionary new]; + + [self addFeature: XMPP_NS_DISCO_ITEMS]; + [self addFeature: XMPP_NS_DISCO_INFO]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_JID release]; + [_node release]; + [_name release]; + [_identities release]; + [_features release]; + [_childNodes release]; + + [super dealloc]; +} + +- (XMPPJID*)JID +{ + OF_GETTER(_JID, YES); +} + +- (OFString*)node +{ + OF_GETTER(_node, YES); +} + +- (OFString*)name +{ + OF_GETTER(_name, YES); +} + +- (OFSortedList*)identities +{ + OF_GETTER(_identities, YES); +} + +- (OFSortedList*)features +{ + OF_GETTER(_features, YES); +} + +- (OFDictionary*)childNodes +{ + OF_GETTER(_childNodes, YES); +} + +- (void)addIdentity: (XMPPDiscoIdentity*)identity +{ + [_identities insertObject: identity]; +} + +- (void)addFeature: (OFString*)feature +{ + [_features insertObject: feature]; +} + +- (void)addChildNode: (XMPPDiscoNode*)node +{ + [_childNodes setObject: node + forKey: [node node]]; +} + +- (BOOL)XMPP_handleItemsIQ: (XMPPIQ*)IQ + connection: (XMPPConnection*)connection +{ + XMPPIQ *resultIQ; + OFXMLElement *response; + XMPPDiscoNode *child; + OFEnumerator *enumerator; + OFXMLElement *query = [IQ elementForName: @"query" + namespace: XMPP_NS_DISCO_ITEMS]; + OFString *node = [[query attributeForName: @"node"] stringValue]; + + if (!(node == _node) && ![node isEqual: _node]) + return NO; + + resultIQ = [IQ resultIQ]; + response = [OFXMLElement elementWithName: @"query" + namespace: XMPP_NS_DISCO_ITEMS]; + [resultIQ addChild: response]; + + enumerator = [_childNodes objectEnumerator]; + while ((child = [enumerator nextObject])) { + OFXMLElement *item = + [OFXMLElement elementWithName: @"item" + namespace: XMPP_NS_DISCO_ITEMS]; + + [item addAttributeWithName: @"jid" + stringValue: [[child JID] fullJID]]; + if ([child node] != nil) + [item addAttributeWithName: @"node" + stringValue: [child node]]; + if ([child name] != nil) + [item addAttributeWithName: @"name" + stringValue: [child name]]; + + [response addChild: item]; + } + + [connection sendStanza: resultIQ]; + + return YES; +} + +- (BOOL)XMPP_handleInfoIQ: (XMPPIQ*)IQ + connection: (XMPPConnection*)connection +{ + XMPPIQ *resultIQ; + OFXMLElement *response; + OFEnumerator *enumerator; + OFString *feature; + XMPPDiscoIdentity *identity; + OFXMLElement *query = [IQ elementForName: @"query" + namespace: XMPP_NS_DISCO_INFO]; + OFString *node = [[query attributeForName: @"node"] stringValue]; + + if (!(node == _node) && ![node isEqual: _node]) + return NO; + + resultIQ = [IQ resultIQ]; + response = [OFXMLElement elementWithName: @"query" + namespace: XMPP_NS_DISCO_INFO]; + [resultIQ addChild: response]; + + enumerator = [_identities objectEnumerator]; + while ((identity = [enumerator nextObject])) { + OFXMLElement *identityElement = + [OFXMLElement elementWithName: @"identity" + namespace: XMPP_NS_DISCO_INFO]; + + [identityElement addAttributeWithName: @"category" + stringValue: [identity category]]; + [identityElement addAttributeWithName: @"type" + stringValue: [identity type]]; + if ([identity name] != nil) + [identityElement addAttributeWithName: @"name" + stringValue: [identity name]]; + + [response addChild: identityElement]; + } + + enumerator = [_features objectEnumerator]; + while ((feature = [enumerator nextObject])) { + OFXMLElement *featureElement = + [OFXMLElement elementWithName: @"feature" + namespace: XMPP_NS_DISCO_INFO]; + [featureElement addAttributeWithName: @"var" + stringValue: feature]; + [response addChild: featureElement]; + } + + [connection sendStanza: resultIQ]; + + return YES; +} +@end Index: src/XMPPJID.m ================================================================== --- src/XMPPJID.m +++ src/XMPPJID.m @@ -227,11 +227,11 @@ - (OFString*)description { return [self fullJID]; } -- (BOOL)isEqual: (id)object +- (bool)isEqual: (id)object { XMPPJID *JID; if (object == self) return YES; Index: src/namespaces.h ================================================================== --- src/namespaces.h +++ src/namespaces.h @@ -20,14 +20,16 @@ * POSSIBILITY OF SUCH DAMAGE. */ #define XMPP_NS_BIND @"urn:ietf:params:xml:ns:xmpp-bind" #define XMPP_NS_CLIENT @"jabber:client" +#define XMPP_NS_DISCO_INFO @"http://jabber.org/protocol/disco#info" +#define XMPP_NS_DISCO_ITEMS @"http://jabber.org/protocol/disco#items" #define XMPP_NS_ROSTER @"jabber:iq:roster" #define XMPP_NS_ROSTERVER @"urn:xmpp:features:rosterver" #define XMPP_NS_SASL @"urn:ietf:params:xml:ns:xmpp-sasl" #define XMPP_NS_SESSION @"urn:ietf:params:xml:ns:xmpp-session" #define XMPP_NS_SM @"urn:xmpp:sm:3" #define XMPP_NS_STARTTLS @"urn:ietf:params:xml:ns:xmpp-tls" #define XMPP_NS_STANZAS @"urn:ietf:params:xml:ns:xmpp-stanzas" #define XMPP_NS_STREAM @"http://etherx.jabber.org/streams" #define XMPP_NS_XMPP_STREAM @"urn:ietf:params:xml:ns:xmpp-streams" Index: tests/test.m ================================================================== --- tests/test.m +++ tests/test.m @@ -24,10 +24,12 @@ #include #import #import "XMPPConnection.h" +#import "XMPPDiscoEntity.h" +#import "XMPPDiscoIdentity.h" #import "XMPPJID.h" #import "XMPPStanza.h" #import "XMPPIQ.h" #import "XMPPMessage.h" #import "XMPPPresence.h" @@ -151,10 +153,53 @@ { of_log(@"Bound to JID: %@", [jid fullJID]); of_log(@"Supports SM: %@", [conn_ supportsStreamManagement] ? @"YES" : @"NO"); + + XMPPDiscoEntity *discoEntity = + [[XMPPDiscoEntity alloc] initWithConnection: conn]; + + [discoEntity addIdentity: + [XMPPDiscoIdentity identityWithCategory: @"client" + type: @"pc" + name: @"ObjXMPP"]]; + + XMPPDiscoNode *nodeMusic = + [XMPPDiscoNode discoNodeWithJID: jid + node: @"music" + name: @"My music"]; + [discoEntity addChildNode: nodeMusic]; + + XMPPDiscoNode *nodeRHCP = + [XMPPDiscoNode discoNodeWithJID: jid + node: @"fa3b6" + name: @"Red Hot Chili Peppers"]; + [nodeMusic addChildNode: nodeRHCP]; + + XMPPDiscoNode *nodeStop = + [XMPPDiscoNode discoNodeWithJID: jid + node: @"qwe87" + name: @"Can't Stop"]; + [nodeRHCP addChildNode: nodeStop]; + + XMPPDiscoNode *nodeClueso = [XMPPDiscoNode discoNodeWithJID: jid + node: @"ea386" + name: @"Clueso"]; + [nodeMusic addChildNode: nodeClueso]; + + XMPPDiscoNode *nodeChicago = [XMPPDiscoNode discoNodeWithJID: jid + node: @"qwr87" + name: @"Chicago"]; + [nodeClueso addChildNode: nodeChicago]; + + [discoEntity addDiscoNode: nodeMusic]; + [discoEntity addDiscoNode: nodeRHCP]; + [discoEntity addDiscoNode: nodeClueso]; + [discoEntity addDiscoNode: nodeStop]; + [discoEntity addDiscoNode: nodeChicago]; + [roster requestRoster]; } - (void)rosterWasReceived: (XMPPRoster*)roster_ {