Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -20,10 +20,11 @@ XMPPRoster.m \ XMPPRosterItem.m \ XMPPSCRAMAuth.m \ XMPPSRVLookup.m \ XMPPStanza.m \ + XMPPStreamManagement.m \ XMPPXMLElementBuilder.m INCLUDES = ${SRCS:.m=.h} \ namespaces.h \ ObjXMPP.h \ Index: src/XMPPConnection.h ================================================================== --- src/XMPPConnection.h +++ src/XMPPConnection.h @@ -156,10 +156,11 @@ XMPPAuthenticator *authModule; BOOL streamOpen; BOOL needsSession; BOOL encryptionRequired, encrypted; BOOL supportsRosterVersioning; + BOOL supportsStreamManagement; unsigned int lastID; /// \endcond } #ifdef OF_HAVE_PROPERTIES @@ -195,10 +196,12 @@ @property BOOL encryptionRequired; /// \brief Whether the connection is encrypted @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 /** * \brief Creates a new autoreleased XMPPConnection. * Index: src/XMPPConnection.m ================================================================== --- src/XMPPConnection.m +++ src/XMPPConnection.m @@ -358,10 +358,15 @@ - (BOOL)supportsRosterVersioning { return supportsRosterVersioning; } + +- (BOOL)supportsStreamManagement +{ + return supportsStreamManagement; +} - (BOOL)checkCertificateAndGetReason: (OFString**)reason { X509Certificate *cert; OFDictionary *SANs; @@ -858,10 +863,14 @@ @throw [OFException exceptionWithClass: [self class]]; if ([element elementForName: @"ver" 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; ADDED src/XMPPStreamManagement.h Index: src/XMPPStreamManagement.h ================================================================== --- /dev/null +++ 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 ADDED src/XMPPStreamManagement.m Index: src/XMPPStreamManagement.m ================================================================== --- /dev/null +++ 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 Index: src/namespaces.h ================================================================== --- src/namespaces.h +++ src/namespaces.h @@ -24,9 +24,10 @@ #define XMPP_NS_CLIENT @"jabber:client" #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 @@ -30,10 +30,11 @@ #import "XMPPStanza.h" #import "XMPPIQ.h" #import "XMPPMessage.h" #import "XMPPPresence.h" #import "XMPPRoster.h" +#import "XMPPStreamManagement.h" #import "XMPPJSONFileStorage.h" @interface AppDelegate: OFObject #ifdef OF_HAVE_OPTIONAL_PROTOCOLS @@ -102,10 +103,12 @@ [conn setDataStorage: storage]; 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]; } @@ -137,14 +140,15 @@ - (void)connectionWasAuthenticated: (XMPPConnection*)conn { 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]; } - (void)rosterWasReceived: (XMPPRoster*)roster_