diff --git a/Extensions/XEP-0030/XMPPIQ+XEP_0030.h b/Extensions/XEP-0030/XMPPIQ+XEP_0030.h new file mode 100644 index 0000000000..30aee4306a --- /dev/null +++ b/Extensions/XEP-0030/XMPPIQ+XEP_0030.h @@ -0,0 +1,17 @@ +// +// XMPPIQ+XEP_0030.h +// XMPPFramework +// +// Created by Andres on 7/07/16. +// Copyright © 2016 Inaka. All rights reserved. +// + +#import +#import "XMPPFramework/XMPPJID.h" + +@interface XMPPIQ (XEP_0030) + ++ (nonnull XMPPIQ *) discoverItemsAssociatedWithJID:(nonnull XMPPJID *)jid; ++ (nonnull NSArray *)parseDiscoveredItemsFromIQ:(nonnull XMPPIQ *)iq; + +@end diff --git a/Extensions/XEP-0030/XMPPIQ+XEP_0030.m b/Extensions/XEP-0030/XMPPIQ+XEP_0030.m new file mode 100644 index 0000000000..b3902be26e --- /dev/null +++ b/Extensions/XEP-0030/XMPPIQ+XEP_0030.m @@ -0,0 +1,49 @@ +// +// XMPPIQ+XEP_0030.m +// XMPPFramework +// +// Created by Andres on 7/07/16. +// Copyright © 2016 Inaka. All rights reserved. +// + +#import "XMPPIQ+XEP_0030.h" +#import "NSXMLElement+XMPP.h" + +#define XMLNS_DISCO_ITEMS @"http://jabber.org/protocol/disco#items" + +@implementation XMPPIQ (XEP_0060) + ++ (nonnull XMPPIQ *) discoverItemsAssociatedWithJID:(nonnull XMPPJID *)jid { + + // + // // disco#items + // + + NSString *iqID = [XMPPStream generateUUID]; + NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns: XMLNS_DISCO_ITEMS]; + return [XMPPIQ iqWithType:@"get" to:jid elementID:iqID child:query]; + +} + ++ (nonnull NSArray *)parseDiscoveredItemsFromIQ:(nonnull XMPPIQ *)iq { + // + // + // + // + // + // + // + // + + NSXMLElement *query = [iq elementForName:@"query" xmlns: XMLNS_DISCO_ITEMS]; + if(query) { + return [query elementsForName:@"item"]; + } +} + +@end diff --git a/Extensions/XEP-0030/XMPPServiceDiscovery.h b/Extensions/XEP-0030/XMPPServiceDiscovery.h deleted file mode 100644 index 4a1bf8e5ba..0000000000 --- a/Extensions/XEP-0030/XMPPServiceDiscovery.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// XMPPServiceDiscovery.h -// Mangosta -// -// Created by Andres Canal on 4/27/16. -// Copyright © 2016 Inaka. All rights reserved. -// - -#import - -@class XMPPIDTracker; - -@interface XMPPServiceDiscovery : XMPPModule { - XMPPIDTracker *xmppIDTracker; -} - -- (void)discoverInformationAboutJID:(XMPPJID *)jid; -- (void)discoverItemsAssociatedWithJID:(XMPPJID *)jid; - -@end - -@protocol XMPPServiceDiscoveryDelegate - -@optional - -- (void)xmppServiceDiscovery:(XMPPServiceDiscovery *)sender didDiscoverInformation:(NSArray *)items; -- (void)xmppServiceDiscovery:(XMPPServiceDiscovery *)sender didDiscoverItems:(NSArray *)items; - -- (void)xmppServiceDiscovery:(XMPPServiceDiscovery *)sender didFailToDiscover:(XMPPIQ *)iq; - -@end \ No newline at end of file diff --git a/Extensions/XEP-0030/XMPPServiceDiscovery.m b/Extensions/XEP-0030/XMPPServiceDiscovery.m deleted file mode 100644 index f398185675..0000000000 --- a/Extensions/XEP-0030/XMPPServiceDiscovery.m +++ /dev/null @@ -1,115 +0,0 @@ -// -// XMPPServiceDiscovery.m -// Mangosta -// -// Created by Andres Canal on 4/27/16. -// Copyright © 2016 Inaka. All rights reserved. -// - -#define XMLNS_DISCO_ITEMS @"http://jabber.org/protocol/disco#items" -#define XMLNS_DISCO_INFO @"http://jabber.org/protocol/disco#info" -#import "XMPPServiceDiscovery.h" -#import "XMPPIDTracker.h" - -@interface XMPPServiceDiscovery() - @property BOOL discoveringInfo; -@end - -@implementation XMPPServiceDiscovery - -- (BOOL)activate:(XMPPStream *)aXmppStream { - - if ([super activate:aXmppStream]) { - xmppIDTracker = [[XMPPIDTracker alloc] initWithDispatchQueue:moduleQueue]; - - return YES; - } - - return NO; -} - -- (void)deactivate { - dispatch_block_t block = ^{ @autoreleasepool { - - [xmppIDTracker removeAllIDs]; - xmppIDTracker = nil; - - }}; - - if (dispatch_get_specific(moduleQueueTag)) - block(); - else - dispatch_sync(moduleQueue, block); - - [super deactivate]; -} - -- (void) discoverInfoOrItem:(NSString *) infoOrItems jid:(XMPPJID *) jid { - - dispatch_block_t block = ^{ @autoreleasepool { - // - // // disco#info - // - - NSString *iqID = [XMPPStream generateUUID]; - NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns: infoOrItems]; - XMPPIQ *iq = [XMPPIQ iqWithType:@"get" to:jid elementID:iqID child:query]; - - [xmppIDTracker addID:iqID - target:self - selector:@selector(handleDiscovery:withInfo:) - timeout:60.0]; - - [xmppStream sendElement:iq]; - }}; - - if (dispatch_get_specific(moduleQueueTag)) - block(); - else - dispatch_async(moduleQueue, block); -} - -- (void)discoverInformationAboutJID:(XMPPJID *)jid{ - self.discoveringInfo = true; - [self discoverInfoOrItem:XMLNS_DISCO_INFO jid:jid]; -} - - -- (void)discoverItemsAssociatedWithJID:(XMPPJID *)jid{ - self.discoveringInfo = false; - [self discoverInfoOrItem:XMLNS_DISCO_ITEMS jid:jid]; -} - -- (void)handleDiscovery:(XMPPIQ *)iq withInfo:(id )info{ - - if ([[iq type] isEqualToString:@"result"]){ - NSXMLElement *query = [iq elementForName:@"query"]; - NSArray *items = [query children]; - - if (self.discoveringInfo) { - [multicastDelegate xmppServiceDiscovery:self didDiscoverInformation:items]; - } else { - [multicastDelegate xmppServiceDiscovery:self didDiscoverItems:items]; - } - - } else { - [multicastDelegate xmppServiceDiscovery:self didFailToDiscover:iq]; - } -} - -- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq -{ - NSString *type = [iq type]; - - if ([type isEqualToString:@"result"] || [type isEqualToString:@"error"]) - { - return [xmppIDTracker invokeForID:[iq elementID] withObject:iq]; - } - - return NO; -} - -@end diff --git a/Xcode/Testing-pod/Podfile.lock b/Xcode/Testing-pod/Podfile.lock index fcc0f32e66..5679c9e180 100644 --- a/Xcode/Testing-pod/Podfile.lock +++ b/Xcode/Testing-pod/Podfile.lock @@ -192,6 +192,6 @@ SPEC CHECKSUMS: CocoaAsyncSocket: a18c75dca4b08723628a0bacca6e94803d90be91 CocoaLumberjack: 97fab7ee5f507fe54445cca7ea80f926729cfd15 KissXML: d19dd6dc65e0dc721ba92b3077b8ebdd240f1c1e - XMPPFramework: 2e6b563c554f3a4c6565b33981c5b527b9c474c4 + XMPPFramework: 7fb5899e41c47a75cdf16be39b7079591738b14f COCOAPODS: 0.39.0 diff --git a/Xcode/Testing-pod/XMPPFrameworkTests.xcodeproj/project.pbxproj b/Xcode/Testing-pod/XMPPFrameworkTests.xcodeproj/project.pbxproj index 4e747a282e..0786459d79 100644 --- a/Xcode/Testing-pod/XMPPFrameworkTests.xcodeproj/project.pbxproj +++ b/Xcode/Testing-pod/XMPPFrameworkTests.xcodeproj/project.pbxproj @@ -11,9 +11,9 @@ 63F50D9E1C6020A100CA0201 /* CapabilitiesHashingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 63F50D9B1C6020A100CA0201 /* CapabilitiesHashingTest.m */; }; 63F50D9F1C6020A100CA0201 /* EncodeDecodeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 63F50D9C1C6020A100CA0201 /* EncodeDecodeTest.m */; }; 63F50DA01C6020A100CA0201 /* XMPPURITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63F50D9D1C6020A100CA0201 /* XMPPURITests.m */; }; + C12017BE1D2EC89A0054B254 /* XMPP0030Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = C12017BD1D2EC89A0054B254 /* XMPP0030Tests.m */; }; C141EB1A1CF76CE900513A66 /* XMPPMockStream.m in Sources */ = {isa = PBXBuildFile; fileRef = C141EB191CF76CE900513A66 /* XMPPMockStream.m */; }; C1EA90581CFDC2F50019BC16 /* XMPPRoomLightTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C1EA90571CFDC2F50019BC16 /* XMPPRoomLightTests.m */; }; - C1FF91F61CF8906000C88DEA /* XMPPServiceDiscoveryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C1FF91F51CF8906000C88DEA /* XMPPServiceDiscoveryTests.m */; }; D92C57A41CC2E0820032DE59 /* XMPPStorageHintTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D92C57A31CC2E0820032DE59 /* XMPPStorageHintTests.m */; }; D9BA12A31D1B55C70095CFE4 /* XMPPvCardTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D9BA12A21D1B55C70095CFE4 /* XMPPvCardTests.m */; }; E09D4B441CFCC4A000D37596 /* XMPPMUCLightTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E09D4B431CFCC4A000D37596 /* XMPPMUCLightTests.m */; }; @@ -32,10 +32,10 @@ 63F50D9C1C6020A100CA0201 /* EncodeDecodeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EncodeDecodeTest.m; sourceTree = ""; }; 63F50D9D1C6020A100CA0201 /* XMPPURITests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMPPURITests.m; sourceTree = ""; }; B02E767684F690CBF1C43DDA /* Pods_XMPPFrameworkTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_XMPPFrameworkTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C12017BD1D2EC89A0054B254 /* XMPP0030Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMPP0030Tests.m; sourceTree = ""; }; C141EB181CF76CE900513A66 /* XMPPMockStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XMPPMockStream.h; sourceTree = ""; }; C141EB191CF76CE900513A66 /* XMPPMockStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMPPMockStream.m; sourceTree = ""; }; C1EA90571CFDC2F50019BC16 /* XMPPRoomLightTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMPPRoomLightTests.m; sourceTree = ""; }; - C1FF91F51CF8906000C88DEA /* XMPPServiceDiscoveryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMPPServiceDiscoveryTests.m; sourceTree = ""; }; D92C57A31CC2E0820032DE59 /* XMPPStorageHintTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMPPStorageHintTests.m; sourceTree = ""; }; D9BA12A21D1B55C70095CFE4 /* XMPPvCardTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMPPvCardTests.m; sourceTree = ""; }; E09D4B431CFCC4A000D37596 /* XMPPMUCLightTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMPPMUCLightTests.m; sourceTree = ""; }; @@ -95,7 +95,7 @@ C1EA90571CFDC2F50019BC16 /* XMPPRoomLightTests.m */, D9BA12A21D1B55C70095CFE4 /* XMPPvCardTests.m */, E0AD9A5A1D09BF0B0067F707 /* XMPPRoomLightCoreDataStorageTests.m */, - C1FF91F51CF8906000C88DEA /* XMPPServiceDiscoveryTests.m */, + C12017BD1D2EC89A0054B254 /* XMPP0030Tests.m */, 637AE2E81C6AC0D50051BF1F /* XMPPPushTests.swift */, 63F50D971C60208200CA0201 /* Info.plist */, C141EB181CF76CE900513A66 /* XMPPMockStream.h */, @@ -230,10 +230,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C12017BE1D2EC89A0054B254 /* XMPP0030Tests.m in Sources */, 63F50D9E1C6020A100CA0201 /* CapabilitiesHashingTest.m in Sources */, E09D4B441CFCC4A000D37596 /* XMPPMUCLightTests.m in Sources */, 63F50D9F1C6020A100CA0201 /* EncodeDecodeTest.m in Sources */, - C1FF91F61CF8906000C88DEA /* XMPPServiceDiscoveryTests.m in Sources */, E0ED56D11CF34D28004C726B /* XMPPHTTPFileUploadTests.m in Sources */, E0ED56D61CF4D333004C726B /* XMPPMessageArchiveManagementTests.m in Sources */, C141EB1A1CF76CE900513A66 /* XMPPMockStream.m in Sources */, diff --git a/Xcode/Testing-pod/XMPPFrameworkTests/XMPP0030Tests.m b/Xcode/Testing-pod/XMPPFrameworkTests/XMPP0030Tests.m new file mode 100644 index 0000000000..f23b445c7a --- /dev/null +++ b/Xcode/Testing-pod/XMPPFrameworkTests/XMPP0030Tests.m @@ -0,0 +1,66 @@ +// +// XMPP0030Tests.m +// XMPPFrameworkTests +// +// Created by Andres Canal on 7/7/16. +// +// + +#import +#import "XMPPFramework/XMPPIQ+XEP_0030.h" + +@interface XMPP0030Tests : XCTestCase + +@end + +@implementation XMPP0030Tests + +- (void)testIQDiscoItem { + + XMPPJID *jid = [XMPPJID jidWithString:@"test@server.com"]; + XMPPIQ *discoItemsIQ = [XMPPIQ discoverItemsAssociatedWithJID:jid]; + + NSXMLElement *queryElement = [discoItemsIQ elementForName:@"query"]; + XCTAssertEqualObjects(queryElement.xmlns, @"http://jabber.org/protocol/disco#items"); + + XCTAssertEqualObjects(discoItemsIQ.to, jid); + XCTAssertEqualObjects(discoItemsIQ.type, @"get"); +} + +- (void)testParsingDiscoItemsResponse { + + // + // + // + // + // + // + // + // + + NSMutableString *s = [NSMutableString string]; + [s appendString: @""]; + [s appendString: @" "]; + [s appendString: @" "]; + [s appendString: @" "]; + [s appendString: @" "]; + [s appendString: @" "]; + [s appendString: @" "]; + [s appendString: @""]; + + NSError *error; + NSXMLDocument *doc = [[NSXMLDocument alloc] initWithXMLString:s options:0 error:&error]; + XMPPIQ *iq = [XMPPIQ iqFromElement:[doc rootElement]]; + + NSArray *parsedItems = [XMPPIQ parseDiscoveredItemsFromIQ:iq]; + XCTAssertEqualObjects([((NSXMLElement *)parsedItems[0]) attributeForName:@"jid"].stringValue, @"muc.erlang-solutions.com"); + XCTAssertEqualObjects([((NSXMLElement *)parsedItems[1]) attributeForName:@"jid"].stringValue, @"muclight.erlang-solutions.com"); + XCTAssertEqualObjects([((NSXMLElement *)parsedItems[2]) attributeForName:@"jid"].stringValue, @"pubsub.erlang-solutions.com"); + XCTAssertEqualObjects([((NSXMLElement *)parsedItems[3]) attributeForName:@"jid"].stringValue, @"vjud.erlang-solutions.com"); +} + +@end diff --git a/Xcode/Testing-pod/XMPPFrameworkTests/XMPPServiceDiscoveryTests.m b/Xcode/Testing-pod/XMPPFrameworkTests/XMPPServiceDiscoveryTests.m deleted file mode 100644 index faca1d3a28..0000000000 --- a/Xcode/Testing-pod/XMPPFrameworkTests/XMPPServiceDiscoveryTests.m +++ /dev/null @@ -1,204 +0,0 @@ -// -// XMPPServiceDiscoveryTests.m -// XMPPFrameworkTests -// -// Created by Andres Canal on 5/27/16. -// -// - -#import -#import "XMPPFramework/XMPPServiceDiscovery.h" -#import "XMPPMockStream.h" - -@interface XMPPServiceDiscoveryTests : XCTestCase -@property (nonatomic, strong) XCTestExpectation *delegateExpectation; -@end - -@implementation XMPPServiceDiscoveryTests - -- (void)setUp { - [super setUp]; -} - -- (void)tearDown { - [super tearDown]; -} - -- (void) testDiscoverInformationAbout{ - self.delegateExpectation = [self expectationWithDescription:@"Information Response"]; - - XMPPMockStream *streamTest = [[XMPPMockStream alloc] init]; - XMPPServiceDiscovery *serviceDiscovery = [[XMPPServiceDiscovery alloc] init]; - [serviceDiscovery activate:streamTest]; - [serviceDiscovery addDelegate:self delegateQueue:dispatch_get_main_queue()]; - - __weak typeof(XMPPMockStream) *weakStreamTest = streamTest; - streamTest.elementReceived = ^void(NSXMLElement *element) { - NSString *elementID = [element attributeForName:@"id"].stringValue; - XMPPIQ *iq = [self fakeInfoIQWithID:elementID]; - [weakStreamTest fakeIQResponse:iq]; - }; - - [serviceDiscovery discoverInformationAboutJID:[XMPPJID jidWithString:@"test.com"]]; - - [self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) { - if(error){ - XCTFail(@"Expectation Failed with error: %@", error); - } - }]; -} - -- (void)xmppServiceDiscovery:(XMPPServiceDiscovery *)sender didDiscoverInformation:(NSArray *)items{ - XCTAssertEqual(items.count, 9); - [self.delegateExpectation fulfill]; -} - -- (void) testDiscoverItems{ - self.delegateExpectation = [self expectationWithDescription:@"Items Response"]; - - XMPPMockStream *streamTest = [[XMPPMockStream alloc] init]; - XMPPServiceDiscovery *serviceDiscovery = [[XMPPServiceDiscovery alloc] init]; - [serviceDiscovery activate:streamTest]; - [serviceDiscovery addDelegate:self delegateQueue:dispatch_get_main_queue()]; - - __weak typeof(XMPPMockStream) *weakStreamTest = streamTest; - streamTest.elementReceived = ^void(NSXMLElement *element) { - NSString *elementID = [element attributeForName:@"id"].stringValue; - XMPPIQ *iq = [self fakeItemIQWithID:elementID]; - [weakStreamTest fakeIQResponse:iq]; - }; - - [serviceDiscovery discoverItemsAssociatedWithJID:[XMPPJID jidWithString:@"test.com"]]; - - [self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) { - if(error){ - XCTFail(@"Expectation Failed with error: %@", error); - } - }]; -} - -- (void)xmppServiceDiscovery:(XMPPServiceDiscovery *)sender didDiscoverItems:(NSArray *)items{ - XCTAssertEqual(items.count, 8); - [self.delegateExpectation fulfill]; -} - -- (void) testError{ - self.delegateExpectation = [self expectationWithDescription:@"Error Response"]; - - XMPPMockStream *streamTest = [[XMPPMockStream alloc] init]; - XMPPServiceDiscovery *serviceDiscovery = [[XMPPServiceDiscovery alloc] init]; - [serviceDiscovery activate:streamTest]; - [serviceDiscovery addDelegate:self delegateQueue:dispatch_get_main_queue()]; - - __weak typeof(XMPPMockStream) *weakStreamTest = streamTest; - streamTest.elementReceived = ^void(NSXMLElement *element) { - NSString *elementID = [element attributeForName:@"id"].stringValue; - XMPPIQ *iq = [self fakeErrorWithID:elementID]; - [weakStreamTest fakeIQResponse:iq]; - }; - - [serviceDiscovery discoverItemsAssociatedWithJID:[XMPPJID jidWithString:@"test.com"]]; - - [self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) { - if(error){ - XCTFail(@"Expectation Failed with error: %@", error); - } - }]; -} - -- (XMPPIQ *)fakeErrorWithID:(NSString *) elementID{ - NSMutableString *s = [NSMutableString string]; - [s appendString:@""]; - [s appendString:@" "]; - [s appendString:@" "]; - [s appendString:@" "]; - [s appendString:@" "]; - [s appendString:@""]; - - NSError *error; - NSXMLDocument *doc = [[NSXMLDocument alloc] initWithXMLString:s options:0 error:&error]; - XMPPIQ *iq = [XMPPIQ iqFromElement:[doc rootElement]]; - [iq addAttributeWithName:@"id" stringValue:elementID]; - - return iq; -} - -- (void)xmppServiceDiscovery:(XMPPServiceDiscovery *)sender didFailToDiscover:(XMPPIQ *)iq{ - XCTAssertNotNil([iq elementForName:@"error"]); - [self.delegateExpectation fulfill]; -} - - -- (XMPPIQ *)fakeItemIQWithID:(NSString *) elementID{ - NSMutableString *s = [NSMutableString string]; - [s appendString:@""]; - [s appendString:@" "]; - [s appendString:@" "]; - [s appendString:@" "]; - [s appendString:@" "]; - [s appendString:@" "]; - [s appendString:@" "]; - [s appendString:@" "]; - [s appendString:@" "]; - [s appendString:@" "]; - [s appendString:@" "]; - [s appendString:@""]; - - NSError *error; - NSXMLDocument *doc = [[NSXMLDocument alloc] initWithXMLString:s options:0 error:&error]; - XMPPIQ *iq = [XMPPIQ iqFromElement:[doc rootElement]]; - [iq addAttributeWithName:@"id" stringValue:elementID]; - - return iq; -} - -- (XMPPIQ *)fakeInfoIQWithID:(NSString *) elementID{ - - NSMutableString *s = [NSMutableString string]; - [s appendString: @""]; - [s appendString: @" "]; - [s appendString: @" "]; - [s appendString: @" "]; - [s appendString: @" "]; - [s appendString: @" "]; - [s appendString: @" "]; - [s appendString: @" "]; - [s appendString: @" "]; - [s appendString: @" "]; - [s appendString: @" "]; - [s appendString: @" "]; - [s appendString: @""]; - - NSError *error; - NSXMLDocument *doc = [[NSXMLDocument alloc] initWithXMLString:s options:0 error:&error]; - XMPPIQ *iq = [XMPPIQ iqFromElement:[doc rootElement]]; - [iq addAttributeWithName:@"id" stringValue:elementID]; - - return iq; -} - -@end