ObjXMPP  Check-in [a618f77f45]

Overview
Comment:Add very basic Stream Management (XEP-0198) support

This implementation only counts incomming stanzas and
sends ACKs on request.
While basic this already allows servers to write
messages to offline storage that were sent to,
but never received by a client.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: a618f77f451105da657b03c25a5420baf37089bdf4c682507674a08de9439bdb
User & Date: js on 2012-08-10 12:08:23
Other Links: manifest | tags
Context
2012-09-20
00:35
Handle the connection in the run loop. check-in: 71cf6454e4 user: js tags: trunk
2012-08-10
12:08
Add very basic Stream Management (XEP-0198) support check-in: a618f77f45 user: js tags: trunk
12:08
Close the stream when restricted XML is received check-in: f848b17360 user: js tags: trunk
Changes

Modified src/Makefile from [542ec87a49] to [b1583dc3ac].

18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
33
34
35
       XMPPPLAINAuth.m		\
       XMPPPresence.m		\
       XMPPRoster.m		\
       XMPPRosterItem.m		\
       XMPPSCRAMAuth.m		\
       XMPPSRVLookup.m		\
       XMPPStanza.m		\

       XMPPXMLElementBuilder.m

INCLUDES = ${SRCS:.m=.h}	\
	   namespaces.h		\
	   ObjXMPP.h		\
	   XMPPStorage.h

include ../buildsys.mk

CPPFLAGS += -I.. -DHAVE_CONFIG_H
LD = ${OBJC}







>











18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
       XMPPPLAINAuth.m		\
       XMPPPresence.m		\
       XMPPRoster.m		\
       XMPPRosterItem.m		\
       XMPPSCRAMAuth.m		\
       XMPPSRVLookup.m		\
       XMPPStanza.m		\
       XMPPStreamManagement.m	\
       XMPPXMLElementBuilder.m

INCLUDES = ${SRCS:.m=.h}	\
	   namespaces.h		\
	   ObjXMPP.h		\
	   XMPPStorage.h

include ../buildsys.mk

CPPFLAGS += -I.. -DHAVE_CONFIG_H
LD = ${OBJC}

Modified src/XMPPConnection.h from [d1760a91eb] to [1fc410e031].

154
155
156
157
158
159
160

161
162
163
164
165
166
167
	XMPPMulticastDelegate *delegates;
	OFMutableDictionary *callbacks;
	XMPPAuthenticator *authModule;
	BOOL streamOpen;
	BOOL needsSession;
	BOOL encryptionRequired, encrypted;
	BOOL supportsRosterVersioning;

	unsigned int lastID;
/// \endcond
}

#ifdef OF_HAVE_PROPERTIES
/// \brief The username to use for authentication
@property (copy) OFString *username;







>







154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
	XMPPMulticastDelegate *delegates;
	OFMutableDictionary *callbacks;
	XMPPAuthenticator *authModule;
	BOOL streamOpen;
	BOOL needsSession;
	BOOL encryptionRequired, encrypted;
	BOOL supportsRosterVersioning;
	BOOL supportsStreamManagement;
	unsigned int lastID;
/// \endcond
}

#ifdef OF_HAVE_PROPERTIES
/// \brief The username to use for authentication
@property (copy) OFString *username;
193
194
195
196
197
198
199


200
201
202
203
204
205
206
@property (readonly, retain, getter=socket) OFTCPSocket *sock;
/// \brief Whether encryption is required
@property BOOL encryptionRequired;
/// \brief Whether the connection is encrypted
@property (readonly) BOOL encrypted;
/// \brief Whether roster versioning is supported
@property (readonly) BOOL supportsRosterVersioning;


#endif

/**
 * \brief Creates a new autoreleased XMPPConnection.
 *
 * \return A new autoreleased XMPPConnection
 */







>
>







194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
@property (readonly, retain, getter=socket) OFTCPSocket *sock;
/// \brief Whether encryption is required
@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.
 *
 * \return A new autoreleased XMPPConnection
 */

Modified src/XMPPConnection.m from [cfbc33f443] to [934072be7a].

356
357
358
359
360
361
362





363
364
365
366
367
368
369
	return streamOpen;
}

- (BOOL)supportsRosterVersioning
{
	return supportsRosterVersioning;
}






- (BOOL)checkCertificateAndGetReason: (OFString**)reason
{
	X509Certificate *cert;
	OFDictionary *SANs;
	BOOL serviceSpecific = NO;








>
>
>
>
>







356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
	return streamOpen;
}

- (BOOL)supportsRosterVersioning
{
	return supportsRosterVersioning;
}

- (BOOL)supportsStreamManagement
{
	return supportsStreamManagement;
}

- (BOOL)checkCertificateAndGetReason: (OFString**)reason
{
	X509Certificate *cert;
	OFDictionary *SANs;
	BOOL serviceSpecific = NO;

856
857
858
859
860
861
862




863
864
865
866
867
868
869
	if (encryptionRequired && !encrypted)
		/* TODO: Find/create an exception to throw here */
		@throw [OFException exceptionWithClass: [self class]];

	if ([element elementForName: @"ver"
			  namespace: XMPP_NS_ROSTERVER] != nil)
		supportsRosterVersioning = YES;





	if (mechs != nil) {
		OFEnumerator *enumerator;
		OFXMLElement *mech;

		enumerator = [[mechs children] objectEnumerator];
		while ((mech = [enumerator nextObject]) != nil)







>
>
>
>







861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
	if (encryptionRequired && !encrypted)
		/* TODO: Find/create an exception to throw here */
		@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;

		enumerator = [[mechs children] objectEnumerator];
		while ((mech = [enumerator nextObject]) != nil)

Added src/XMPPStreamManagement.h version [b1e66795db].











































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*
 * Copyright (c) 2012, Florian Zeitz <florob@babelmonkeys.de>
 *
 * 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
    <XMPPConnectionDelegate>
#endif
{
/// \cond internal
	XMPPConnection *connection;
	uint32_t receivedCount;
/// \endcond
}

- initWithConnection: (XMPPConnection*)connection;
@end

Added src/XMPPStreamManagement.m version [b0ffeb590a].











































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/*
 * Copyright (c) 2012, Florian Zeitz <florob@babelmonkeys.de>
 *
 * 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

Modified src/namespaces.h from [847696acaf] to [28e73e8b28].

22
23
24
25
26
27
28

29
30
31
32

#define XMPP_NS_BIND @"urn:ietf:params:xml:ns:xmpp-bind"
#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_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"







>




22
23
24
25
26
27
28
29
30
31
32
33

#define XMPP_NS_BIND @"urn:ietf:params:xml:ns:xmpp-bind"
#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"

Modified tests/test.m from [7a00751abf] to [4505fb0d79].

28
29
30
31
32
33
34

35
36
37
38
39
40
41
#import "XMPPConnection.h"
#import "XMPPJID.h"
#import "XMPPStanza.h"
#import "XMPPIQ.h"
#import "XMPPMessage.h"
#import "XMPPPresence.h"
#import "XMPPRoster.h"

#import "XMPPJSONFileStorage.h"

@interface AppDelegate: OFObject
#ifdef OF_HAVE_OPTIONAL_PROTOCOLS
    <OFApplicationDelegate, XMPPConnectionDelegate, XMPPRosterDelegate>
#endif
{







>







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#import "XMPPConnection.h"
#import "XMPPJID.h"
#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
    <OFApplicationDelegate, XMPPConnectionDelegate, XMPPRosterDelegate>
#endif
{
100
101
102
103
104
105
106


107
108
109
110
111
112
113
	XMPPJSONFileStorage *storage =
	    [[XMPPJSONFileStorage alloc] initWithFile: @"storage.json"];
	[conn setDataStorage: storage];

	roster = [[XMPPRoster alloc] initWithConnection: conn];
	[roster addDelegate: self];



	if ([arguments count] != 3) {
		of_log(@"Invalid count of command line arguments!");
		[OFApplication terminateWithStatus: 1];
	}

	[conn setDomain: [arguments objectAtIndex: 0]];
	[conn setUsername: [arguments objectAtIndex: 1]];







>
>







101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
	XMPPJSONFileStorage *storage =
	    [[XMPPJSONFileStorage alloc] initWithFile: @"storage.json"];
	[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];
	}

	[conn setDomain: [arguments objectAtIndex: 0]];
	[conn setUsername: [arguments objectAtIndex: 1]];
135
136
137
138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
}

- (void)connectionWasAuthenticated: (XMPPConnection*)conn
{
	of_log(@"Auth successful");
}

- (void)connection: (XMPPConnection*)conn
     wasBoundToJID: (XMPPJID*)jid
{
	of_log(@"Bound to JID: %@", [jid fullJID]);


	[roster requestRoster];
}

- (void)rosterWasReceived: (XMPPRoster*)roster_
{
	XMPPPresence *pres;







|



>







138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
}

- (void)connectionWasAuthenticated: (XMPPConnection*)conn
{
	of_log(@"Auth successful");
}

- (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_
{
	XMPPPresence *pres;