diff --git a/src/Makefile b/src/Makefile index 7473246..dc04314 100644 --- a/src/Makefile +++ b/src/Makefile @@ -22,6 +22,7 @@ SRCS = XMPPAuthenticator.m \ XMPPSCRAMAuth.m \ XMPPSRVLookup.m \ XMPPStanza.m \ + XMPPStreamManagement.m \ XMPPXMLElementBuilder.m INCLUDES = ${SRCS:.m=.h} \ diff --git a/src/XMPPConnection.h b/src/XMPPConnection.h index 62032f9..07f9fd4 100644 --- a/src/XMPPConnection.h +++ b/src/XMPPConnection.h @@ -158,6 +158,7 @@ BOOL needsSession; BOOL encryptionRequired, encrypted; BOOL supportsRosterVersioning; + BOOL supportsStreamManagement; unsigned int lastID; /// \endcond } @@ -197,6 +198,8 @@ @property (readonly) BOOL encrypted; /// \brief Whether roster versioning is supported @property (readonly) BOOL supportsRosterVersioning; +/// \brief Whether stream management is supported +@property (readonly) BOOL supportsStreamManagement; #endif /** diff --git a/src/XMPPConnection.m b/src/XMPPConnection.m index 0b9eb8e..d6fbc0d 100644 --- a/src/XMPPConnection.m +++ b/src/XMPPConnection.m @@ -361,6 +361,11 @@ return supportsRosterVersioning; } +- (BOOL)supportsStreamManagement +{ + return supportsStreamManagement; +} + - (BOOL)checkCertificateAndGetReason: (OFString**)reason { X509Certificate *cert; @@ -861,6 +866,10 @@ namespace: XMPP_NS_ROSTERVER] != nil) supportsRosterVersioning = YES; + if ([element elementForName: @"sm" + namespace: XMPP_NS_SM] != nil) + supportsStreamManagement = YES; + if (mechs != nil) { OFEnumerator *enumerator; OFXMLElement *mech; diff --git a/src/XMPPStreamManagement.h b/src/XMPPStreamManagement.h new file mode 100644 index 0000000..f3b2118 --- /dev/null +++ b/src/XMPPStreamManagement.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2012, 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" + +@interface XMPPStreamManagement: OFObject +#ifdef OF_HAVE_OPTIONAL_PROTOCOLS + +#endif +{ +/// \cond internal + XMPPConnection *connection; + uint32_t receivedCount; +/// \endcond +} + +- initWithConnection: (XMPPConnection*)connection; +@end diff --git a/src/XMPPStreamManagement.m b/src/XMPPStreamManagement.m new file mode 100644 index 0000000..664162f --- /dev/null +++ b/src/XMPPStreamManagement.m @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2012, 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 "XMPPStreamManagement.h" +#import "namespaces.h" + +@implementation XMPPStreamManagement +- initWithConnection: (XMPPConnection*)connection_ +{ + self = [super init]; + + @try { + connection = connection_; + [connection addDelegate: self]; + receivedCount = 0; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [connection removeDelegate: self]; + + [super dealloc]; +} + +- (void)connection: (XMPPConnection*)connection_ + didReceiveElement: (OFXMLElement*)element +{ + OFString *elementName = [element name]; + OFString *elementNS = [element namespace]; + + if ([elementNS isEqual: XMPP_NS_SM]) { + if ([elementName isEqual: @"enabled"]) { + receivedCount = 0; + return; + } + + if ([elementName isEqual: @"failed"]) { + /* TODO: How do we handle this? */ + return; + } + + if ([elementName isEqual: @"r"]) { + OFXMLElement *ack = + [OFXMLElement elementWithName: @"a" + namespace: XMPP_NS_SM]; + [ack addAttributeWithName: @"h" + stringValue: + [OFString stringWithFormat: @"%" PRIu32, + receivedCount]]; + [connection_ sendStanza: ack]; + } + } + + if ([elementNS isEqual: XMPP_NS_CLIENT] && + ([elementName isEqual: @"iq"] || + [elementName isEqual: @"presence"] || + [elementName isEqual: @"message"])) + receivedCount++; +} + +/* TODO: Count outgoing stanzas here and cache them, send own ACK requests +- (void)connection: (XMPPConnection*)connection_ + didSendElement: (OFXMLElement*)element +{ +} +*/ + +- (void)connection: (XMPPConnection*)connection_ + wasBoundToJID: (XMPPJID*)jid +{ + if ([connection_ supportsStreamManagement]) + [connection_ sendStanza: + [OFXMLElement elementWithName: @"enable" + namespace: XMPP_NS_SM]]; +} +@end diff --git a/src/namespaces.h b/src/namespaces.h index d9884b0..b67c451 100644 --- a/src/namespaces.h +++ b/src/namespaces.h @@ -26,6 +26,7 @@ #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" diff --git a/tests/test.m b/tests/test.m index 00acd20..39bfed4 100644 --- a/tests/test.m +++ b/tests/test.m @@ -32,6 +32,7 @@ #import "XMPPMessage.h" #import "XMPPPresence.h" #import "XMPPRoster.h" +#import "XMPPStreamManagement.h" #import "XMPPJSONFileStorage.h" @interface AppDelegate: OFObject @@ -104,6 +105,8 @@ OF_APPLICATION_DELEGATE(AppDelegate) roster = [[XMPPRoster alloc] initWithConnection: conn]; [roster addDelegate: self]; + [[XMPPStreamManagement alloc] initWithConnection: conn]; + if ([arguments count] != 3) { of_log(@"Invalid count of command line arguments!"); [OFApplication terminateWithStatus: 1]; @@ -139,10 +142,11 @@ OF_APPLICATION_DELEGATE(AppDelegate) of_log(@"Auth successful"); } -- (void)connection: (XMPPConnection*)conn +- (void)connection: (XMPPConnection*)conn_ wasBoundToJID: (XMPPJID*)jid { of_log(@"Bound to JID: %@", [jid fullJID]); + of_log(@"Supports SM: %@", [conn_ supportsStreamManagement] ? @"YES" : @"NO"); [roster requestRoster]; }