ObjXMPP  Check-in [2faf18cba7]

Overview
Comment:Adjust to ObjFW changes
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 2faf18cba740568fdb3ecd3cc4bb6fdfe94aa9f4324e55268bd0722b14a81876
User & Date: js on 2017-05-13 15:03:44
Other Links: manifest | tags
Context
2017-07-23
11:19
Adjust to ObjFW changes & small fixes check-in: efaee4fc4c user: js tags: trunk
2017-05-13
15:03
Adjust to ObjFW changes check-in: 2faf18cba7 user: js tags: trunk
2017-04-30
15:04
Fix URL in copyright header check-in: 4663b00cb4 user: js tags: trunk
Changes

Modified src/XMPPAuthenticator.h from [32835f9423] to [d2168a1fd5].

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


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







+
+









-
+

-
+

-
+









-
-
+
+










-
-
-
+
+
+






-
+







-
+

+
+
 * 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 <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

/**
 * \brief A base class for classes implementing authentication mechanisms
 */
@interface XMPPAuthenticator: OFObject
{
	OFString *_authzid, *_authcid, *_password;
}

/// \brief The authzid to get authorization for
@property (copy) OFString *authzid;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *authzid;
/// \brief The authcid to authenticate with
@property (copy) OFString *authcid;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *authcid;
/// \brief The password to authenticate with
@property (copy) OFString *password;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *password;

/**
 * \brief Initializes an already allocated XMPPAuthenticator with an authcid
 *	  and password.
 *
 * \param authcid The authcid to authenticate with
 * \param password The password to authenticate with
 * \return A initialized XMPPAuthenticator
 */
- initWithAuthcid: (OFString*)authcid
	 password: (OFString*)password;
- initWithAuthcid: (nullable OFString *)authcid
	 password: (nullable OFString *)password;

/**
 * \brief Initializes an already allocated XMPPSCRAMAuthenticator with an
 *	  authzid, authcid and password.
 *
 * \param authzid The authzid to get authorization for
 * \param authcid The authcid to authenticate with
 * \param password The password to authenticate with
 * \return A initialized XMPPAuthenticator
 */
- initWithAuthzid: (OFString*)authzid
	  authcid: (OFString*)authcid
	 password: (OFString*)password;
- initWithAuthzid: (nullable OFString *)authzid
	  authcid: (nullable OFString *)authcid
	 password: (nullable OFString *)password OF_DESIGNATED_INITIALIZER;

/**
 * \brief Returns an OFDataArray containing the initial authentication message.
 *
 * \return An OFDataAray containing the initial authentication message
 */
- (OFDataArray*)initialMessage;
- (OFDataArray *)initialMessage;

/**
 * \brief Continue authentication with the specified data.
 *
 * \param data The continuation data send by the server
 * \return The appropriate response if the data was a challenge, nil otherwise
 */
- (OFDataArray*)continueWithData: (OFDataArray*)data;
- (nullable OFDataArray *)continueWithData: (OFDataArray *)data;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPAuthenticator.m from [ff883875bb] to [8f69cece0e].

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
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







-
-
+
+






-
-
-
+
+
+







#endif

#import "XMPPAuthenticator.h"

@implementation XMPPAuthenticator
@synthesize authzid = _authzid, authcid = _authcid, password = _password;

- initWithAuthcid: (OFString*)authcid
	 password: (OFString*)password
- initWithAuthcid: (OFString *)authcid
	 password: (OFString *)password
{
	return [self initWithAuthzid: nil
			     authcid: authcid
			    password: password];
}

- initWithAuthzid: (OFString*)authzid
	  authcid: (OFString*)authcid
	 password: (OFString*)password
- initWithAuthzid: (OFString *)authzid
	  authcid: (OFString *)authcid
	 password: (OFString *)password
{
	self = [super init];

	@try {
		_authzid = [authzid copy];
		_authcid = [authcid copy];
		_password = [password copy];
61
62
63
64
65
66
67
68

69
70
71
72
73

74
75
76
77
61
62
63
64
65
66
67

68
69
70
71
72

73
74
75
76
77







-
+




-
+




	[_authzid release];
	[_authcid release];
	[_password release];

	[super dealloc];
}

- (OFDataArray*)initialMessage
- (OFDataArray *)initialMessage
{
	return nil;
}

- (OFDataArray*)continueWithData: (OFDataArray*)challenge
- (OFDataArray *)continueWithData: (OFDataArray *)challenge
{
	return nil;
}
@end

Modified src/XMPPCallback.h from [48a5d16844] to [ee73746306].

18
19
20
21
22
23
24


25
26
27
28
29


30
31
32
33
34
35
36
18
19
20
21
22
23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39







+
+




-
+
+







 * 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 <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@class XMPPConnection;
@class XMPPIQ;

#ifdef OF_HAVE_BLOCKS
typedef void (^xmpp_callback_block_t)(XMPPConnection*, XMPPIQ*);
typedef void (^xmpp_callback_block_t)(XMPPConnection *_Nonnull,
    XMPPIQ *_Nonnull);
#endif

@interface XMPPCallback: OFObject
{
	id _target;
	SEL _selector;
#ifdef OF_HAVE_BLOCKS
44
45
46
47
48
49
50
51
52


53


47
48
49
50
51
52
53


54
55
56
57
58







-
-
+
+

+
+
#endif

+ (instancetype)callbackWithTarget: (id)target
			  selector: (SEL)selector;
- initWithTarget: (id)target
	selector: (SEL)selector;

- (void)runWithIQ: (XMPPIQ*)iq
       connection: (XMPPConnection*)connection;
- (void)runWithIQ: (XMPPIQ *)iq
       connection: (XMPPConnection *)connection;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPCallback.m from [e144c7f667] to [7b928d9476].

26
27
28
29
30
31
32

33

34
35
36
37
38
39
40
26
27
28
29
30
31
32
33

34
35
36
37
38
39
40
41







+
-
+








#import "XMPPCallback.h"

@implementation XMPPCallback
#ifdef OF_HAVE_BLOCKS
+ (instancetype)callbackWithBlock: (xmpp_callback_block_t)block
{
	return [[(XMPPCallback *)[self alloc]
	return [[(XMPPCallback*)[self alloc] initWithBlock: block] autorelease];
	    initWithBlock: block] autorelease];
}

- initWithBlock: (xmpp_callback_block_t)block
{
	self = [super init];

	@try {
72
73
74
75
76
77
78
79
80


81
82
83
84

85
86
87
88
89

90
91
73
74
75
76
77
78
79


80
81
82
83
84

85
86
87
88
89

90
91
92







-
-
+
+



-
+




-
+


#ifdef OF_HAVE_BLOCKS
	[_block release];
#endif

	[super dealloc];
}

- (void)runWithIQ: (XMPPIQ*)iq
       connection: (XMPPConnection*)connection
- (void)runWithIQ: (XMPPIQ *)IQ
       connection: (XMPPConnection *)connection
{
#ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		_block(connection, iq);
		_block(connection, IQ);
	else
#endif
		[_target performSelector: _selector
			      withObject: connection
			      withObject: iq];
			      withObject: IQ];
}
@end

Modified src/XMPPConnection.h from [d8b74359c0] to [423e3fef12].

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
102
103
104
105
106
107
108


109
110
111
112
113
114
115

116
117
118
119
120
121
122
123
124
125
126
127


128
129
130
131
132
133
134
135

136
137
138
139
140
141
142

143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

164
165
166
167
168
169
170
171
172
173
174

175
176

177
178
179
180
181
182

183
184

185
186

187
188

189
190

191
192

193
194

195
196
197
198

199
200

201
202
203
204
205
206
207
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
102
103
104
105


106
107
108
109
110
111
112
113

114
115
116
117
118
119
120
121
122
123
124


125
126
127
128
129
130
131
132
133

134
135
136
137
138
139
140

141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

162
163
164
165
166
167
168
169
170
171
172

173
174

175
176
177
178
179
180

181
182

183
184

185
186

187
188

189
190

191
192

193
194
195
196

197
198

199
200
201
202
203
204
205
206







+
+










-
-
+
+


-
-
-







-
-
+
+







-
-
+
+






-
+







-
-
+
+







-
-
+
+








-
-
+
+







-
-
+
+






-
+










-
-
+
+







-
+






-
+




















-
+










-
+

-
+





-
+

-
+

-
+

-
+

-
+

-
+

-
+



-
+

-
+







 */

#import <ObjFW/ObjFW.h>

#import "XMPPCallback.h"
#import "XMPPStorage.h"

OF_ASSUME_NONNULL_BEGIN

@class XMPPConnection;
@class XMPPJID;
@class XMPPIQ;
@class XMPPMessage;
@class XMPPPresence;
@class XMPPAuthenticator;
@class SSLSocket;
@class XMPPMulticastDelegate;

/**
 * \brief A protocol that should be (partially) implemented
 *	  by delegates of a XMPPConnection
 * \brief A protocol that should be (partially) implemented by delegates of a
 *	  @ref XMPPConnection
 */
@protocol XMPPConnectionDelegate
#ifndef XMPP_CONNECTION_M
    <OFObject>
#endif
@optional
/**
 * \brief This callback is called when the connection received an element.
 *
 * \param connection The connection that received the element
 * \param element The element that was received
 */
-  (void)connection: (XMPPConnection*)connection
  didReceiveElement: (OFXMLElement*)element;
-  (void)connection: (XMPPConnection *)connection
  didReceiveElement: (OFXMLElement *)element;

/**
 * \brief This callback is called when the connection sent an element.
 *
 * \param connection The connection that sent the element
 * \param element The element that was sent
 */
- (void)connection: (XMPPConnection*)connection
    didSendElement: (OFXMLElement*)element;
- (void)connection: (XMPPConnection *)connection
    didSendElement: (OFXMLElement *)element;

/**
 * \brief This callback is called when the connection sucessfully authenticated.
 *
 * \param connection The connection that was authenticated
 */
- (void)connectionWasAuthenticated: (XMPPConnection*)connection;
- (void)connectionWasAuthenticated: (XMPPConnection *)connection;

/**
 * \brief This callback is called when the connection was bound to a JID.
 *
 * \param connection The connection that was bound to a JID
 * \param JID The JID the conecction was bound to
 */
- (void)connection: (XMPPConnection*)connection
     wasBoundToJID: (XMPPJID*)JID;
- (void)connection: (XMPPConnection *)connection
     wasBoundToJID: (XMPPJID *)JID;

/**
 * \brief This callback is called when the connection received an IQ stanza.
 *
 * \param connection The connection that received the stanza
 * \param iq The IQ stanza that was received
 */
- (bool)connection: (XMPPConnection*)connection
      didReceiveIQ: (XMPPIQ*)iq;
- (bool)connection: (XMPPConnection *)connection
      didReceiveIQ: (XMPPIQ *)iq;

/**
 * \brief This callback is called when the connection received a presence
 *	  stanza.
 *
 * \param connection The connection that received the stanza
 * \param presence The presence stanza that was received
 */
-   (void)connection: (XMPPConnection*)connection
  didReceivePresence: (XMPPPresence*)presence;
-   (void)connection: (XMPPConnection *)connection
  didReceivePresence: (XMPPPresence *)presence;

/**
 * \brief This callback is called when the connection received a message stanza.
 *
 * \param connection The connection that received the stanza
 * \param message The message stanza that was received
 */
-  (void)connection: (XMPPConnection*)connection
  didReceiveMessage: (XMPPMessage*)message;
-  (void)connection: (XMPPConnection *)connection
  didReceiveMessage: (XMPPMessage *)message;

/**
 * \brief This callback is called when the connection was closed.
 *
 * \param connection The connection that was closed
 */
- (void)connectionWasClosed: (XMPPConnection*)connection;
- (void)connectionWasClosed: (XMPPConnection *)connection;

/*!
 * @brief This callback is called when the connection threw an exception.
 *
 * This is only called for connections on which
 * @ref XMPPConnection::handleConnection has been called.
 *
 * @param connection The connection which threw an exception
 * @param exception The exception the connection threw
 */
-  (void)connection: (XMPPConnection*)connection
  didThrowException: (OFException*)exception;
-  (void)connection: (XMPPConnection *)connection
  didThrowException: (OFException *)exception;

/**
 * \brief This callback is called when the connection is about to upgrade to
 *	  TLS.
 *
 * \param connection The connection that will upgraded to TLS
 */
- (void)connectionWillUpgradeToTLS: (XMPPConnection*)connection;
- (void)connectionWillUpgradeToTLS: (XMPPConnection *)connection;

/**
 * \brief This callback is called when the connection was upgraded to use TLS.
 *
 * \param connection The connection that was upgraded to TLS
 */
- (void)connectionDidUpgradeToTLS: (XMPPConnection*)connection;
- (void)connectionDidUpgradeToTLS: (XMPPConnection *)connection;
@end

/**
 * \brief A class which abstracts a connection to an XMPP service.
 */
@interface XMPPConnection: OFObject <OFXMLParserDelegate,
    OFXMLElementBuilderDelegate>
{
	id _socket;
	OFXMLParser *_parser, *_oldParser;
	OFXMLElementBuilder *_elementBuilder, *_oldElementBuilder;
	OFString *_username, *_password, *_server, *_resource;
	OFString *_privateKeyFile, *_certificateFile;
	const char *_privateKeyPassphrase;
	OFString *_domain, *_domainToASCII;
	XMPPJID *_JID;
	uint16_t _port;
	id <XMPPStorage> _dataStorage;
	OFString *_language;
	XMPPMulticastDelegate *_delegates;
	OFMutableDictionary *_callbacks;
	OFMutableDictionary OF_GENERIC(OFString *, XMPPCallback *) *_callbacks;
	XMPPAuthenticator *_authModule;
	bool _streamOpen;
	bool _needsSession;
	bool _encryptionRequired, _encrypted;
	bool _supportsRosterVersioning;
	bool _supportsStreamManagement;
	unsigned int _lastID;
}

/// \brief The username to use for authentication
@property (copy) OFString *username;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *username;
/// \brief The password to use for authentication
@property (copy) OFString *password;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *password;
/**
 * \brief The server to use for the connection
 *
 * This is useful if the address of the server is different from the domain.
 */
@property (copy) OFString *server;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *server;
/// \brief The domain to connect to
@property (copy) OFString *domain;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *domain;
/// \brief The resource to request for the connection
@property (copy) OFString *resource;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *resource;
/// \brief The language to request for the connection
@property (copy) OFString *language;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *language;
/// \brief A private key file to use for authentication
@property (copy) OFString *privateKeyFile;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *privateKeyFile;
/// \brief A certificate file to use for authentication
@property (copy) OFString *certificateFile;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *certificateFile;
/// \brief The JID the server assigned to the connection after binding
@property (copy, readonly) XMPPJID *JID;
@property (readonly, nonatomic) XMPPJID *JID;
/// \brief The port to connect to
@property uint16_t port;
/// \brief An object for data storage, conforming to the XMPPStorage protocol
@property (assign) id <XMPPStorage> dataStorage;
@property OF_NULLABLE_PROPERTY (assign) id <XMPPStorage> dataStorage;
/// \brief The socket used for the connection
@property (readonly, retain) OFTCPSocket *socket;
@property (readonly, nonatomic) OFTCPSocket *socket;
/// \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
243
244
245
246
247
248
249
250


251
252
253
254
255
256
257
242
243
244
245
246
247
248

249
250
251
252
253
254
255
256
257







-
+
+







 *	  pointer to the reason why the certificate is not valid
 *
 * \param reason A pointer to an OFString which is set to a reason in case the
 *		 certificate is not valid (otherwise, it does not touch it).
 *		 Passing NULL means the reason is not stored anywhere.
 * \return Whether the certificate is valid
 */
- (bool)checkCertificateAndGetReason: (OFString**)reason;
- (bool)checkCertificateAndGetReason:
    (OFString *__autoreleasing _Nonnull *_Nullable)reason;

/**
 * \brief Adds the connection to the run loop.
 */
- (void)handleConnection;

/**
265
266
267
268
269
270
271
272

273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308

309
310
311
312
313
314
315
316

317
318

319
320
321
322
323
324
325
326
327
328
329

330
331
332
333
334
335
336
337
338

339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383

265
266
267
268
269
270
271

272
273
274




























275
276
277
278
279

280
281
282
283
284
285
286
287

288
289

290
291
292
293
294
295
296
297
298
299
300

301
302
303
304
305
306
307
308
309

310









































311
312


313







-
+


-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-





-
+







-
+

-
+










-
+








-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-


-
-
+
 *
 * This is useful for handling multiple connections at once.
 *
 * \param buffer The buffer to parse
 * \param length The length of the buffer. If length is 0, it is assumed that
 *		 the connection was closed.
 */
- (void)parseBuffer: (const void*)buffer
- (void)parseBuffer: (const void *)buffer
	     length: (size_t)length;

/**
 * \brief Returns the socket used by the XMPPConnection.
 *
 * \return The socket used by the XMPPConnection
 */
- (OFTCPSocket*)socket;

/**
 * \brief Returns whether encryption is encrypted.
 *
 * \return Whether encryption is encrypted
 */
- (bool)encryptionRequired;

/**
 * \brief Sets whether encryption is required.
 *
 * \param required Whether encryption is required
 */
- (void)setEncryptionRequired: (bool)required;

/**
 * \brief Returns whether the connection is encrypted.
 *
 * \return Whether the connection is encrypted
 */
- (bool)encrypted;

/**
 * \brief Sends an OFXMLElement, usually an XMPPStanza.
 *
 * \param element The element to send
 */
- (void)sendStanza: (OFXMLElement*)element;
- (void)sendStanza: (OFXMLElement *)element;

/*!
 * @brief Sends an XMPPIQ, registering a callback method.
 *
 * @param IQ The IQ to send
 * @param target The object that contains the callback method
 * @param selector The selector of the callback method,
 *		   must take exactly one parameter of type XMPPIQ*
 *		   must take exactly one parameter of type `XMPPIQ *`
 */
-   (void)sendIQ: (XMPPIQ*)IQ
-   (void)sendIQ: (XMPPIQ *)IQ
  callbackTarget: (id)target
	selector: (SEL)selector;

#ifdef OF_HAVE_BLOCKS
/*!
 * @brief Sends an XMPPIQ, registering a callback block.
 *
 * @param IQ The IQ to send
 * @param block The callback block
 */
-  (void)sendIQ: (XMPPIQ*)IQ
-  (void)sendIQ: (XMPPIQ *)IQ
  callbackBlock: (xmpp_callback_block_t)block;
#endif

/**
 * \brief Generates a new, unique stanza ID.
 *
 * \return A new, generated, unique stanza ID.
 */
- (OFString*)generateStanzaID;
- (OFString *)generateStanzaID;

- (void)setUsername: (OFString*)username;
- (OFString*)username;
- (void)setPassword: (OFString*)password;
- (OFString*)password;
- (void)setServer: (OFString*)server;
- (OFString*)server;
- (void)setDomain: (OFString*)domain;
- (OFString*)domain;
- (void)setResource: (OFString*)resource;
- (OFString*)resource;
- (XMPPJID*)JID;
- (void)setPort: (uint16_t)port;
- (uint16_t)port;
- (void)setDataStorage: (id <XMPPStorage>)dataStorage;
- (id <XMPPStorage>)dataStorage;
- (void)setLanguage: (OFString*)language;
- (OFString*)language;
- (bool)supportsRosterVersioning;
- (bool)supportsStreamManagement;

- (void)XMPP_startStream;
- (void)XMPP_handleStream: (OFXMLElement*)element;
- (void)XMPP_handleTLS: (OFXMLElement*)element;
- (void)XMPP_handleSASL: (OFXMLElement*)element;
- (void)XMPP_handleStanza: (OFXMLElement*)element;
- (void)XMPP_sendAuth: (OFString*)authName;
- (void)XMPP_sendResourceBind;
- (void)XMPP_sendStreamError: (OFString*)condition
			text: (OFString*)text;
- (void)XMPP_handleIQ: (XMPPIQ*)iq;
- (void)XMPP_handleMessage: (XMPPMessage*)message;
- (void)XMPP_handlePresence: (XMPPPresence*)presence;
- (void)XMPP_handleFeatures: (OFXMLElement*)element;
- (void)XMPP_handleResourceBindForConnection: (XMPPConnection*)connection
					  IQ: (XMPPIQ*)iq;
- (void)XMPP_sendSession;
- (void)XMPP_handleSessionForConnection: (XMPPConnection*)connection
				     IQ: (XMPPIQ*)iq;
- (OFString*)XMPP_IDNAToASCII: (OFString*)domain;
- (XMPPMulticastDelegate*)XMPP_delegates;
@end

@interface OFObject (XMPPConnectionDelegate) <XMPPConnectionDelegate>
@end
OF_ASSUME_NONNULL_END

Modified src/XMPPConnection.m from [26c4d1cce6] to [e5df95b15f].

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
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
102
103
104
105
106
107
108







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
-
+
+


+
+

-
-
+
+







#import "XMPPExceptions.h"
#import "XMPPXMLElementBuilder.h"
#import "namespaces.h"

#import <ObjFW/macros.h>

#define BUFFER_LENGTH 512

OF_ASSUME_NONNULL_BEGIN

@interface XMPPConnection ()
- (void)XMPP_startStream;
- (void)XMPP_handleStream: (OFXMLElement *)element;
- (void)XMPP_handleTLS: (OFXMLElement *)element;
- (void)XMPP_handleSASL: (OFXMLElement *)element;
- (void)XMPP_handleStanza: (OFXMLElement *)element;
- (void)XMPP_sendAuth: (OFString *)authName;
- (void)XMPP_sendResourceBind;
- (void)XMPP_sendStreamError: (OFString *)condition
			text: (nullable OFString *)text;
- (void)XMPP_handleIQ: (XMPPIQ *)IQ;
- (void)XMPP_handleMessage: (XMPPMessage *)message;
- (void)XMPP_handlePresence: (XMPPPresence *)presence;
- (void)XMPP_handleFeatures: (OFXMLElement *)element;
- (void)XMPP_handleResourceBindForConnection: (XMPPConnection *)connection
					  IQ: (XMPPIQ *)IQ;
- (void)XMPP_sendSession;
- (void)XMPP_handleSessionForConnection: (XMPPConnection *)connection
				     IQ: (XMPPIQ *)IQ;
- (OFString *)XMPP_IDNAToASCII: (OFString *)domain;
- (XMPPMulticastDelegate *)XMPP_delegates;
@end

@interface XMPPConnection_ConnectThread: OFThread
{
	OFThread *_sourceThread;
	XMPPConnection *_connection;
}

- initWithSourceThread: (OFThread*)sourceThread
	    connection: (XMPPConnection*)connection;
- initWithSourceThread: (OFThread *)sourceThread
	    connection: (XMPPConnection *)connection;
@end

OF_ASSUME_NONNULL_END

@implementation XMPPConnection_ConnectThread
- initWithSourceThread: (OFThread*)sourceThread
	    connection: (XMPPConnection*)connection
- initWithSourceThread: (OFThread *)sourceThread
	    connection: (XMPPConnection *)connection
{
	self = [super init];

	@try {
		_sourceThread = [sourceThread retain];
		_connection = [connection retain];
	} @catch (id e) {
114
115
116
117
118
119
120


121

122
123
124
125
126
127
128
141
142
143
144
145
146
147
148
149

150
151
152
153
154
155
156
157







+
+
-
+







	[pool release];

	return nil;
}
@end

@implementation XMPPConnection
@synthesize username = _username, resource = _resource, server = _server;
@synthesize domain = _domain, password = _password, language = _language;
@synthesize language = _language, privateKeyFile = _privateKeyFile;
@synthesize privateKeyFile = _privateKeyFile;
@synthesize certificateFile = _certificateFile, socket = _socket;
@synthesize encryptionRequired = _encryptionRequired, encrypted = _encrypted;
@synthesize supportsRosterVersioning = _supportsRosterVersioning;
@synthesize supportsStreamManagement = _supportsStreamManagement;

+ (instancetype)connection
{
161
162
163
164
165
166
167
168

169
170
171
172
173
174
175
190
191
192
193
194
195
196

197
198
199
200
201
202
203
204







-
+







	[_delegates release];
	[_callbacks release];
	[_authModule release];

	[super dealloc];
}

- (void)setUsername: (OFString*)username
- (void)setUsername: (OFString *)username
{
	OFString *old = _username;

	if (username != nil) {
		char *node;
		Stringprep_rc rc;

187
188
189
190
191
192
193
194
195
196
197
198
199

200
201
202
203
204
205
206
216
217
218
219
220
221
222






223
224
225
226
227
228
229
230







-
-
-
-
-
-
+







		}
	} else
		_username = nil;

	[old release];
}

- (OFString*)username
{
	return [[_username copy] autorelease];
}

- (void)setResource: (OFString*)resource
- (void)setResource: (OFString *)resource
{
	OFString *old = _resource;

	if (resource != nil) {
		char *res;
		Stringprep_rc rc;

218
219
220
221
222
223
224
225
226
227
228
229
230

231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247

248
249
250
251
252
253
254
242
243
244
245
246
247
248






249
250
251
252
253
254
255
256
257
258
259
260






261
262
263
264
265
266
267
268







-
-
-
-
-
-
+











-
-
-
-
-
-
+







		}
	} else
		_resource = nil;

	[old release];
}

- (OFString*)resource
{
	return [[_resource copy] autorelease];
}

- (void)setServer: (OFString*)server
- (void)setServer: (OFString *)server
{
	OFString *old = _server;

	if (server != nil)
		_server = [self XMPP_IDNAToASCII: server];
	else
		_server = nil;

	[old release];
}

- (OFString*)server
{
	return [[_server copy] autorelease];
}

- (void)setDomain: (OFString*)domain
- (void)setDomain: (OFString *)domain
{
	OFString *oldDomain = _domain;
	OFString *oldDomainToASCII = _domainToASCII;

	if (domain != nil) {
		char *srv;
		Stringprep_rc rc;
272
273
274
275
276
277
278
279
280
281
282
283
284

285
286
287
288
289
290
291
286
287
288
289
290
291
292






293
294
295
296
297
298
299
300







-
-
-
-
-
-
+







		_domainToASCII = nil;
	}

	[oldDomain release];
	[oldDomainToASCII release];
}

- (OFString*)domain
{
	return [[_domain copy] autorelease];
}

- (void)setPassword: (OFString*)password
- (void)setPassword: (OFString *)password
{
	OFString *old = _password;

	if (password != nil) {
		char *pass;
		Stringprep_rc rc;

303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
312
313
314
315
316
317
318





319
320
321
322
323
324
325







-
-
-
-
-







		}
	} else
		_password = nil;

	[old release];
}

- (OFString*)password
{
	return [[_password copy] autorelease];
}

- (void)connect
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	XMPPSRVEntry *candidate = nil;
	XMPPSRVLookup *SRVLookup = nil;
	OFEnumerator *enumerator;

378
379
380
381
382
383
384
385

386
387
388
389
390
391
392
382
383
384
385
386
387
388

389
390
391
392
393
394
395
396







-
+







	[[[[XMPPConnection_ConnectThread alloc]
	    initWithSourceThread: [OFThread currentThread]
		      connection: self] autorelease] start];

	[pool release];
}

-  (bool)XMPP_parseBuffer: (const void*)buffer
-  (bool)XMPP_parseBuffer: (const void *)buffer
		   length: (size_t)length
{
	if ([_socket isAtEndOfStream]) {
		[_delegates broadcastSelector: @selector(connectionWasClosed:)
				   withObject: self];
		return false;
	}
400
401
402
403
404
405
406
407

408
409
410
411
412
413
414
415
416
417
418
419
420
421


422
423

424
425
426
427
428
429
430
404
405
406
407
408
409
410

411
412
413
414
415
416
417
418
419
420
421
422
423


424
425
426

427
428
429
430
431
432
433
434







-
+












-
-
+
+

-
+







		[self close];
		return false;
	}

	return true;
}

- (void)parseBuffer: (const void*)buffer
- (void)parseBuffer: (const void *)buffer
	     length: (size_t)length
{
	[self XMPP_parseBuffer: buffer
			length: length];

	[_oldParser release];
	[_oldElementBuilder release];

	_oldParser = nil;
	_oldElementBuilder = nil;
}

-      (bool)stream: (OFStream*)stream
  didReadIntoBuffer: (char*)buffer
-      (bool)stream: (OFStream *)stream
  didReadIntoBuffer: (char *)buffer
	     length: (size_t)length
	  exception: (OFException*)exception
	  exception: (OFException *)exception
{
	if (exception != nil) {
		[_delegates broadcastSelector: @selector(connection:
						   didThrowException:)
				   withObject: self
				   withObject: exception];
		[self close];
465
466
467
468
469
470
471
472

473
474
475
476
477
478
479
469
470
471
472
473
474
475

476
477
478
479
480
481
482
483







-
+







}

- (bool)streamOpen
{
	return _streamOpen;
}

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

	@try {
		[_socket verifyPeerCertificate];
501
502
503
504
505
506
507
508

509
510
511
512
513
514
515
516
517

518
519
520
521
522
523
524
505
506
507
508
509
510
511

512
513
514
515
516
517
518
519
520

521
522
523
524
525
526
527
528







-
+








-
+







	if (!serviceSpecific &&
	    [cert hasCommonNameMatchingDomain: _domainToASCII])
		return true;

	return false;
}

- (void)sendStanza: (OFXMLElement*)element
- (void)sendStanza: (OFXMLElement *)element
{
	[_delegates broadcastSelector: @selector(connection:didSendElement:)
			   withObject: self
			   withObject: element];

	[_socket writeString: [element XMLString]];
}

-   (void)sendIQ: (XMPPIQ*)IQ
-   (void)sendIQ: (XMPPIQ *)IQ
  callbackTarget: (id)target
	selector: (SEL)selector
{
	OFAutoreleasePool *pool;
	XMPPCallback *callback;
	OFString *ID, *key;

541
542
543
544
545
546
547
548

549
550
551
552
553
554
555
545
546
547
548
549
550
551

552
553
554
555
556
557
558
559







-
+







		       forKey: key];
	[pool release];

	[self sendStanza: IQ];
}

#ifdef OF_HAVE_BLOCKS
-  (void)sendIQ: (XMPPIQ*)IQ
-  (void)sendIQ: (XMPPIQ *)IQ
  callbackBlock: (xmpp_callback_block_t)block
{
	OFAutoreleasePool *pool;
	XMPPCallback *callback;
	OFString *ID, *key;

	pool = [[OFAutoreleasePool alloc] init];
570
571
572
573
574
575
576
577

578
579
580
581
582
583
584
585
586





587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604

605
606
607
608
609
610

611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626

627
628
629
630


631
632
633
634
635
636
637
574
575
576
577
578
579
580

581
582
583
584
585





586
587
588
589
590
591



592
593
594
595
596
597
598
599
600
601
602
603
604

605
606
607
608
609
610

611

612
613
614
615
616
617
618
619
620
621
622
623
624
625

626
627
628


629
630
631
632
633
634
635
636
637







-
+




-
-
-
-
-
+
+
+
+
+

-
-
-













-
+





-
+
-














-
+


-
-
+
+







		       forKey: key];
	[pool release];

	[self sendStanza: IQ];
}
#endif

- (OFString*)generateStanzaID
- (OFString *)generateStanzaID
{
	return [OFString stringWithFormat: @"objxmpp_%u", _lastID++];
}

-    (void)parser: (OFXMLParser*)p
  didStartElement: (OFString*)name
	   prefix: (OFString*)prefix
	namespace: (OFString*)ns
       attributes: (OFArray*)attributes
-    (void)parser: (OFXMLParser *)parser
  didStartElement: (OFString *)name
	   prefix: (OFString *)prefix
	namespace: (OFString *)NS
       attributes: (OFArray *)attributes
{
	OFEnumerator *enumerator;
	OFXMLAttribute *attribute;

	if (![name isEqual: @"stream"]) {
		// No dedicated stream error for this, may not even be XMPP
		[self close];
		[_socket close];
		return;
	}

	if (![prefix isEqual: @"stream"]) {
		[self XMPP_sendStreamError: @"bad-namespace-prefix"
				      text: nil];
		return;
	}

	if (![ns isEqual: XMPP_NS_STREAM]) {
	if (![NS isEqual: XMPP_NS_STREAM]) {
		[self XMPP_sendStreamError: @"invalid-namespace"
				      text: nil];
		return;
	}

	enumerator = [attributes objectEnumerator];
	for (OFXMLAttribute *attribute in attributes) {
	while ((attribute = [enumerator nextObject]) != nil) {
		if ([[attribute name] isEqual: @"from"] &&
		    ![[attribute stringValue] isEqual: _domain]) {
			[self XMPP_sendStreamError: @"invalid-from"
					      text: nil];
			return;
		}
		if ([[attribute name] isEqual: @"version"] &&
		    ![[attribute stringValue] isEqual: @"1.0"]) {
			[self XMPP_sendStreamError: @"unsupported-version"
					      text: nil];
			return;
		}
	}

	[_parser setDelegate: _elementBuilder];
	[parser setDelegate: _elementBuilder];
}

- (void)elementBuilder: (OFXMLElementBuilder*)builder
       didBuildElement: (OFXMLElement*)element
- (void)elementBuilder: (OFXMLElementBuilder *)builder
       didBuildElement: (OFXMLElement *)element
{
	/* Ignore whitespace elements */
	if ([element name] == nil)
		return;

	[element setDefaultNamespace: XMPP_NS_CLIENT];
	[element setPrefix: @"stream"
650
651
652
653
654
655
656
657
658
659
660




661
662
663
664
665
666
667
650
651
652
653
654
655
656




657
658
659
660
661
662
663
664
665
666
667







-
-
-
-
+
+
+
+







	if ([[element namespace] isEqual: XMPP_NS_STARTTLS])
		[self XMPP_handleTLS: element];

	if ([[element namespace] isEqual: XMPP_NS_SASL])
		[self XMPP_handleSASL: element];
}

- (void)elementBuilder: (OFXMLElementBuilder*)builder
  didNotExpectCloseTag: (OFString*)name
		prefix: (OFString*)prefix
	     namespace: (OFString*)ns
- (void)elementBuilder: (OFXMLElementBuilder *)builder
  didNotExpectCloseTag: (OFString *)name
		prefix: (OFString *)prefix
	     namespace: (OFString *)ns
{
	if (![name isEqual: @"stream"] || ![prefix isEqual: @"stream"] ||
	    ![ns isEqual: XMPP_NS_STREAM])
		@throw [OFMalformedXMLException exception];
	else {
		[self close];
	}
718
719
720
721
722
723
724
725

726
727
728
729
730
731
732
718
719
720
721
722
723
724

725
726
727
728
729
730
731
732







-
+







	[_JID release];
	_JID = nil;
	_streamOpen = _needsSession = _encrypted = false;
	_supportsRosterVersioning = _supportsStreamManagement = false;
	_lastID = 0;
}

- (void)XMPP_handleStanza: (OFXMLElement*)element
- (void)XMPP_handleStanza: (OFXMLElement *)element
{
	if ([[element name] isEqual: @"iq"]) {
		[self XMPP_handleIQ: [XMPPIQ stanzaWithElement: element]];
		return;
	}

	if ([[element name] isEqual: @"message"]) {
742
743
744
745
746
747
748
749

750
751
752
753
754
755
756
742
743
744
745
746
747
748

749
750
751
752
753
754
755
756







-
+







	}

	[self XMPP_sendStreamError: @"unsupported-stanza-type"
			      text: nil];
}


- (void)XMPP_handleStream: (OFXMLElement*)element
- (void)XMPP_handleStream: (OFXMLElement *)element
{
	if ([[element name] isEqual: @"features"]) {
		[self XMPP_handleFeatures: element];
		return;
	}

	if ([[element name] isEqual: @"error"]) {
846
847
848
849
850
851
852
853

854
855
856
857
858
859
860
846
847
848
849
850
851
852

853
854
855
856
857
858
859
860







-
+







				     reason: reason];
		return;
	}

	assert(0);
}

- (void)XMPP_handleTLS: (OFXMLElement*)element
- (void)XMPP_handleTLS: (OFXMLElement *)element
{
	if ([[element name] isEqual: @"proceed"]) {
		/* FIXME: Catch errors here */
		SSLSocket *newSock;

		[_delegates broadcastSelector: @selector(
						   connectionWillUpgradeToTLS:)
887
888
889
890
891
892
893
894

895
896
897
898
899
900
901
887
888
889
890
891
892
893

894
895
896
897
898
899
900
901







-
+







	if ([[element name] isEqual: @"failure"])
		/* TODO: Find/create an exception to throw here */
		@throw [OFException exception];

	assert(0);
}

- (void)XMPP_handleSASL: (OFXMLElement*)element
- (void)XMPP_handleSASL: (OFXMLElement *)element
{
	if ([[element name] isEqual: @"challenge"]) {
		OFXMLElement *responseTag;
		OFDataArray *challenge = [OFDataArray
		    dataArrayWithBase64EncodedString: [element stringValue]];
		OFDataArray *response = [_authModule
		    continueWithData: challenge];
935
936
937
938
939
940
941
942

943
944
945
946
947
948
949
935
936
937
938
939
940
941

942
943
944
945
946
947
948
949







-
+







		    exceptionWithConnection: self
				     reason: [element XMLString]];
	}

	assert(0);
}

- (void)XMPP_handleIQ: (XMPPIQ*)IQ
- (void)XMPP_handleIQ: (XMPPIQ *)IQ
{
	bool handled = false;
	XMPPCallback *callback;
	OFString *key;

	if ((key = [[IQ from] fullJID]) == nil)
		key = [_JID bareJID];
966
967
968
969
970
971
972
973

974
975
976
977
978
979
980

981
982
983
984
985
986
987

988
989
990
991
992
993
994
966
967
968
969
970
971
972

973
974
975
976
977
978
979

980
981
982
983
984
985
986

987
988
989
990
991
992
993
994







-
+






-
+






-
+







	if (!handled && ![[IQ type] isEqual: @"error"] &&
	    ![[IQ type] isEqual: @"result"]) {
		[self sendStanza: [IQ errorIQWithType: @"cancel"
					    condition: @"service-unavailable"]];
	}
}

- (void)XMPP_handleMessage: (XMPPMessage*)message
- (void)XMPP_handleMessage: (XMPPMessage *)message
{
	[_delegates broadcastSelector: @selector(connection:didReceiveMessage:)
			   withObject: self
			   withObject: message];
}

- (void)XMPP_handlePresence: (XMPPPresence*)presence
- (void)XMPP_handlePresence: (XMPPPresence *)presence
{
	[_delegates broadcastSelector: @selector(connection:didReceivePresence:)
			   withObject: self
			   withObject: presence];
}

- (void)XMPP_handleFeatures: (OFXMLElement*)element
- (void)XMPP_handleFeatures: (OFXMLElement *)element
{
	OFXMLElement *startTLS = [element elementForName: @"starttls"
					       namespace: XMPP_NS_STARTTLS];
	OFXMLElement *bind = [element elementForName: @"bind"
					   namespace: XMPP_NS_BIND];
	OFXMLElement *session = [element elementForName: @"session"
					      namespace: XMPP_NS_SESSION];
1019
1020
1021
1022
1023
1024
1025
1026

1027
1028
1029
1030
1031
1032
1033
1019
1020
1021
1022
1023
1024
1025

1026
1027
1028
1029
1030
1031
1032
1033







-
+







		OFEnumerator *enumerator;
		OFXMLElement *mech;

		enumerator = [[mechs children] objectEnumerator];
		while ((mech = [enumerator nextObject]) != nil)
			[mechanisms addObject: [mech stringValue]];

		if (_privateKeyFile && _certificateFile &&
		if (_privateKeyFile != nil && _certificateFile != nil &&
		    [mechanisms containsObject: @"EXTERNAL"]) {
			_authModule = [[XMPPEXTERNALAuth alloc] init];
			[self XMPP_sendAuth: @"EXTERNAL"];
			return;
		}

		if ([mechanisms containsObject: @"SCRAM-SHA-1-PLUS"]) {
1059
1060
1061
1062
1063
1064
1065
1066
1067


1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078

1079
1080
1081
1082
1083
1084
1085
1059
1060
1061
1062
1063
1064
1065


1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077

1078
1079
1080
1081
1082
1083
1084
1085







-
-
+
+










-
+







			[self XMPP_sendAuth: @"PLAIN"];
			return;
		}

		assert(0);
	}

	if (session != nil && ![session elementForName: @"optional"
					     namespace: XMPP_NS_SESSION])
	if (session != nil && [session elementForName: @"optional"
					    namespace: XMPP_NS_SESSION] == nil)
		_needsSession = true;

	if (bind != nil) {
		[self XMPP_sendResourceBind];
		return;
	}

	assert(0);
}

- (void)XMPP_sendAuth: (OFString*)authName
- (void)XMPP_sendAuth: (OFString *)authName
{
	OFXMLElement *authTag;
	OFDataArray *initialMessage = [_authModule initialMessage];

	authTag = [OFXMLElement elementWithName: @"auth"
				      namespace: XMPP_NS_SASL];
	[authTag addAttributeWithName: @"mechanism"
1115
1116
1117
1118
1119
1120
1121
1122
1123


1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143


1144
1145

1146
1147
1148

1149
1150

1151
1152
1153
1154
1155

1156
1157

1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175




1176

1177

1178
1179
1180
1181
1182
1183


1184
1185
1186


1187
1188
1189
1190
1191
1192
1193

1194
1195
1196
1197
1198
1199
1200
1115
1116
1117
1118
1119
1120
1121


1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141


1142
1143
1144

1145

1146

1147
1148

1149
1150
1151
1152
1153

1154
1155

1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169





1170
1171
1172
1173
1174
1175

1176
1177
1178
1179
1180


1181
1182
1183


1184
1185
1186
1187
1188
1189
1190
1191

1192
1193
1194
1195
1196
1197
1198
1199







-
-
+
+


















-
-
+
+

-
+
-

-
+

-
+




-
+

-
+













-
-
-
-
-
+
+
+
+

+
-
+




-
-
+
+

-
-
+
+






-
+








	[self	    sendIQ: IQ
	    callbackTarget: self
		  selector: @selector(XMPP_handleResourceBindForConnection:
				IQ:)];
}

- (void)XMPP_sendStreamError: (OFString*)condition
			text: (OFString*)text
- (void)XMPP_sendStreamError: (OFString *)condition
			text: (OFString *)text
{
	OFXMLElement *error = [OFXMLElement
	    elementWithName: @"error"
		  namespace: XMPP_NS_STREAM];
	[error setPrefix: @"stream"
	    forNamespace: XMPP_NS_STREAM];
	[error addChild: [OFXMLElement elementWithName: condition
					     namespace: XMPP_NS_XMPP_STREAM]];
	if (text)
		[error addChild: [OFXMLElement
		    elementWithName: @"text"
			  namespace: XMPP_NS_XMPP_STREAM
			stringValue: text]];
	[_parser setDelegate: nil];
	[self sendStanza: error];
	[self close];
}

- (void)XMPP_handleResourceBindForConnection: (XMPPConnection*)connection
					  IQ: (XMPPIQ*)iq
- (void)XMPP_handleResourceBindForConnection: (XMPPConnection *)connection
					  IQ: (XMPPIQ *)IQ
{
	OFXMLElement *bindElement;
	OFXMLElement *bindElement, *JIDElement;
	OFXMLElement *jidElement;

	assert([[iq type] isEqual: @"result"]);
	assert([[IQ type] isEqual: @"result"]);

	bindElement = [iq elementForName: @"bind"
	bindElement = [IQ elementForName: @"bind"
			       namespace: XMPP_NS_BIND];

	assert(bindElement != nil);

	jidElement = [bindElement elementForName: @"jid"
	JIDElement = [bindElement elementForName: @"jid"
				       namespace: XMPP_NS_BIND];
	_JID = [[XMPPJID alloc] initWithString: [jidElement stringValue]];
	_JID = [[XMPPJID alloc] initWithString: [JIDElement stringValue]];

	if (_needsSession) {
		[self XMPP_sendSession];
		return;
	}

	[_delegates broadcastSelector: @selector(connection:wasBoundToJID:)
			   withObject: self
			   withObject: _JID];
}

- (void)XMPP_sendSession
{
	XMPPIQ *iq;

	iq = [XMPPIQ IQWithType: @"set"
			     ID: [self generateStanzaID]];
	[iq addChild: [OFXMLElement elementWithName: @"session"
	XMPPIQ *IQ = [XMPPIQ IQWithType: @"set"
				     ID: [self generateStanzaID]];

	[IQ addChild: [OFXMLElement elementWithName: @"session"
					  namespace: XMPP_NS_SESSION]];

	[self	    sendIQ: iq
	[self	    sendIQ: IQ
	    callbackTarget: self
		  selector: @selector(XMPP_handleSessionForConnection:IQ:)];
}

- (void)XMPP_handleSessionForConnection: (XMPPConnection*)connection
				     IQ: (XMPPIQ*)iq
- (void)XMPP_handleSessionForConnection: (XMPPConnection *)connection
				     IQ: (XMPPIQ *)IQ
{
	if (![[iq type] isEqual: @"result"])
		assert(0);
	if (![[IQ type] isEqual: @"result"])
		OF_ENSURE(0);

	[_delegates broadcastSelector: @selector(connection:wasBoundToJID:)
			   withObject: self
			   withObject: _JID];
}

- (OFString*)XMPP_IDNAToASCII: (OFString*)domain
- (OFString *)XMPP_IDNAToASCII: (OFString *)domain
{
	OFString *ret;
	char *cDomain;
	Idna_rc rc;

	if ((rc = idna_to_ascii_8z([domain UTF8String],
	    &cDomain, IDNA_USE_STD3_ASCII_RULES)) != IDNA_SUCCESS)
1232
1233
1234
1235
1236
1237
1238
1239

1240
1241
1242
1243
1231
1232
1233
1234
1235
1236
1237

1238
1239
1240
1241
1242







-
+




}

- (void)removeDelegate: (id <XMPPConnectionDelegate>)delegate
{
	[_delegates removeDelegate: delegate];
}

- (XMPPMulticastDelegate*)XMPP_delegates
- (XMPPMulticastDelegate *)XMPP_delegates
{
	return _delegates;
}
@end

Added src/XMPPContact+Private.h version [19b0cc5ea8].














1
2
3
4
5
6
7
8
9
10
11
12
13
+
+
+
+
+
+
+
+
+
+
+
+
+
#import "XMPPContact.h"

OF_ASSUME_NONNULL_BEGIN

@interface XMPPContact ()
- (void)XMPP_setRosterItem: (XMPPRosterItem *)rosterItem;
- (void)XMPP_setPresence: (XMPPPresence *)presence
		resource: (OFString *)resource;
- (void)XMPP_removePresenceForResource: (OFString *)resource;
- (void)XMPP_setLockedOnJID: (nullable XMPPJID *)JID;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPContact.h from [160c8854c7] to [3375b65268].

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
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











+
+

















-
+

-
+







-
-
+
+
+

-
-
+
-
-
-
-
 * 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 <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@class XMPPConnection;
@class XMPPJID;
@class XMPPRosterItem;
@class XMPPMessage;
@class XMPPPresence;

/**
 * \brief A class describing a contact tracked by a XMPPContactManager
 */
@interface XMPPContact: OFObject
{
	XMPPRosterItem *_rosterItem;
	OFMutableDictionary *_presences;
	XMPPJID *_lockedOnJID;
}

/// \brief The XMPPRosterItem corresponding to this contact
@property (readonly) XMPPRosterItem *rosterItem;
@property (readonly, nonatomic) XMPPRosterItem *rosterItem;
/// \brief The XMPPPresences of this contact with the resources as keys
@property (readonly) OFDictionary *presences;
@property (readonly, nonatomic) OFDictionary *presences;

/**
 * \brief Sends a message to the contact honoring resource locking
 *
 * \param message The message to send
 * \param connection The connection to use for sending the message
 */
- (void)sendMessage: (XMPPMessage*)message
	 connection: (XMPPConnection*)connection;
- (void)sendMessage: (XMPPMessage *)message
	 connection: (XMPPConnection *)connection;
@end

- (void)XMPP_setRosterItem: (XMPPRosterItem*)rosterItem;
- (void)XMPP_setPresence: (XMPPPresence*)presence
OF_ASSUME_NONNULL_END
		resource: (OFString*)resource;
- (void)XMPP_removePresenceForResource: (OFString*)resource;
- (void)XMPP_setLockedOnJID: (XMPPJID*)JID;
@end

Modified src/XMPPContact.m from [c409a8ce2b] to [f961d31c3a].

18
19
20
21
22
23
24

25
26
27
28
29
30
31
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32







+







 * 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 "XMPPContact.h"
#import "XMPPContact+Private.h"
#import "XMPPMessage.h"
#import "XMPPConnection.h"

@implementation XMPPContact
@synthesize rosterItem = _rosterItem;
@synthesize presences = _presences;

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
102
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
102
103







-
-
+
+









-
+






-
-
+
+











-
+











-
+






- (void)dealloc
{
	[_presences release];

	[super dealloc];
}

- (void)sendMessage: (XMPPMessage*)message
	 connection: (XMPPConnection*)connection
- (void)sendMessage: (XMPPMessage *)message
	 connection: (XMPPConnection *)connection
{
	if (_lockedOnJID == nil)
		[message setTo: [_rosterItem JID]];
	else
		[message setTo: _lockedOnJID];

	[connection sendStanza: message];
}

- (void)XMPP_setRosterItem: (XMPPRosterItem*)rosterItem
- (void)XMPP_setRosterItem: (XMPPRosterItem *)rosterItem
{
	XMPPRosterItem *old = _rosterItem;
	_rosterItem = [rosterItem retain];
	[old release];
}

- (void)XMPP_setPresence: (XMPPPresence*)presence
		resource: (OFString*)resource
- (void)XMPP_setPresence: (XMPPPresence *)presence
		resource: (OFString *)resource
{
	if (resource != nil)
		[_presences setObject: presence
			       forKey: resource];
	else
		[_presences setObject: presence
			       forKey: @""];

	[self XMPP_setLockedOnJID: nil];
}

- (void)XMPP_removePresenceForResource: (OFString*)resource
- (void)XMPP_removePresenceForResource: (OFString *)resource
{
	if (resource != nil) {
		[_presences removeObjectForKey: resource];
	} else {
		[_presences release];
		_presences = [[OFMutableDictionary alloc] init];
	}

	[self XMPP_setLockedOnJID: nil];
}

- (void)XMPP_setLockedOnJID: (XMPPJID*)JID;
- (void)XMPP_setLockedOnJID: (XMPPJID *)JID;
{
	XMPPJID *old = _lockedOnJID;
	_lockedOnJID = [JID retain];
	[old release];
}
@end

Modified src/XMPPContactManager.h from [13b68822fb] to [ffd20ba38a].

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
102
103
104
105
106
107
108
109
110
111
112
113


114
115
116
117
118
119
120
121
122
123


124
125
126


127
128
129
130
131
132
133
134
135
136
137
138
139
140
141


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
102
103
104
105
106
107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
122
123
124


125
126
127


128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146







+
+

















-
-
+
+








-
-
+
+







-
-
+
+








-
-
+
+







-
-
+
+







-
-
+
+


















-
+
+








-
-
+
+

-
-
+
+















+
+
 */

#import <ObjFW/ObjFW.h>

#import "XMPPConnection.h"
#import "XMPPRoster.h"

OF_ASSUME_NONNULL_BEGIN

@class XMPPContact;
@class XMPPContactManager;
@class XMPPMulticastDelegate;
@class XMPPPresence;

/**
 * \brief A protocol that should be (partially) implemented by delegates
 *	  of a XMPPContactManager
 */
@protocol XMPPContactManagerDelegate <OFObject>
@optional
/**
 * \brief This callback is called whenever a new contact enters the users roster
 *
 * \param manager The contact manager that added the contact
 * \param contact The contact that was added
 */
- (void)contactManager: (XMPPContactManager*)manager
	 didAddContact: (XMPPContact*)contact;
- (void)contactManager: (XMPPContactManager *)manager
	 didAddContact: (XMPPContact *)contact;

/**
 * \brief This callback is called whenever a contact is no longer present in
 *	  the users roster
 *
 * \param manager The contact manager that removed the contact
 * \param contact The contact that was removed
 */
- (void)contactManager: (XMPPContactManager*)manager
      didRemoveContact: (XMPPContact*)contact;
- (void)contactManager: (XMPPContactManager *)manager
      didRemoveContact: (XMPPContact *)contact;

/**
 * \brief This callback is called when a subscription request is received
 *
 * \param manager The contact manager that received the request
 * \param presence The type=subscribe presence
 */
-          (void)contactManager: (XMPPContactManager*)manager
  didReceiveSubscriptionRequest: (XMPPPresence*)presence;
-          (void)contactManager: (XMPPContactManager *)manager
  didReceiveSubscriptionRequest: (XMPPPresence *)presence;

/**
 * \brief This callback is called whenever a contact is about to change its
 *	  roster item
 *
 * \param contact The contact about to updated its roster item
 * \param rosterItem The roster item the contact is going to update with
 */
-	     (void)contact: (XMPPContact*)contact
  willUpdateWithRosterItem: (XMPPRosterItem*)rosterItem;
-	     (void)contact: (XMPPContact *)contact
  willUpdateWithRosterItem: (XMPPRosterItem *)rosterItem;

/**
 * \brief This callback is called whenever a contact send a presence stanza
 *
 * \param contact The contact that send the presence
 * \param presence The presence which was send by the contact
 */
-   (void)contact: (XMPPContact*)contact
  didSendPresence: (XMPPPresence*)presence;
-   (void)contact: (XMPPContact *)contact
  didSendPresence: (XMPPPresence *)presence;

/**
 * \brief This callback is called whenever a contact send a message stanza
 *
 * \param contact The contact that send the message
 * \param message The message which was send by the contact
 */
-  (void)contact: (XMPPContact*)contact
  didSendMessage: (XMPPMessage*)message;
-  (void)contact: (XMPPContact *)contact
  didSendMessage: (XMPPMessage *)message;
@end

/**
 * \brief A class tracking a XMPPContact instance for each contact in the roster
 *
 * This class delegates to a XMPPConnection and a XMPPRoster, thereby tracking
 * each contacts presences and the current XMPPRosterItem.
 */
@interface XMPPContactManager: OFObject <XMPPConnectionDelegate,
    XMPPRosterDelegate>
{
	OFMutableDictionary *_contacts;
	XMPPConnection *_connection;
	XMPPRoster *_roster;
	XMPPMulticastDelegate *_delegates;
}

/// \brief The tracked contacts, with their bare JID as key
@property (readonly) OFDictionary *contacts;
@property (readonly, nonatomic)
    OFDictionary OF_GENERIC(OFString *, XMPPContact *) *contacts;

/*!
 * @brief Initializes an already allocated XMPPContactManager.
 *
 * @param connection The connection to be used to track contacts
 * @param roster The roster used by the contact manager
 * @return An initialized XMPPContactManager
 */
- initWithConnection: (XMPPConnection*)connection
	      roster: (XMPPRoster*)roster;
- initWithConnection: (XMPPConnection *)connection
	      roster: (XMPPRoster *)roster OF_DESIGNATED_INITIALIZER;

- (void)sendSubscribedToJID: (XMPPJID*)subscriber;
- (void)sendUnsubscribedToJID: (XMPPJID*)subscriber;
- (void)sendSubscribedToJID: (XMPPJID *)subscriber;
- (void)sendUnsubscribedToJID: (XMPPJID *)subscriber;

/**
 * \brief Adds the specified delegate.
 *
 * \param delegate The delegate to add
 */
- (void)addDelegate: (id <XMPPContactManagerDelegate>)delegate;

/**
 * \brief Removes the specified delegate.
 *
 * \param delegate The delegate to remove
 */
- (void)removeDelegate: (id <XMPPContactManagerDelegate>)delegate;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPContactManager.m from [3f134700d1] to [e40b256e89].

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
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







+

-
+








-
-
+
+







 * 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 "XMPPContactManager.h"
#import "XMPPContact.h"
#import "XMPPContactManager.h"
#import "XMPPContact+Private.h"
#import "XMPPJID.h"
#import "XMPPMulticastDelegate.h"
#import "XMPPPresence.h"
#import "XMPPRosterItem.h"

@implementation XMPPContactManager
@synthesize contacts = _contacts;

- initWithConnection: (XMPPConnection*)connection
	      roster: (XMPPRoster*)roster
- initWithConnection: (XMPPConnection *)connection
	      roster: (XMPPRoster *)roster
{
	self = [super init];

	@try {
		_connection = connection;
		[_connection addDelegate: self];
		_roster = roster;
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
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







-
+






-
+

















-
+







	[_delegates release];
	[_contacts release];

	[super dealloc];
}


- (void)sendSubscribedToJID: (XMPPJID*)subscriber
- (void)sendSubscribedToJID: (XMPPJID *)subscriber
{
	XMPPPresence *presence = [XMPPPresence presenceWithType: @"subscribed"];
	[presence setTo: subscriber];
	[_connection sendStanza: presence];
}

- (void)sendUnsubscribedToJID: (XMPPJID*)subscriber
- (void)sendUnsubscribedToJID: (XMPPJID *)subscriber
{
	XMPPPresence *presence =
	    [XMPPPresence presenceWithType: @"unsubscribed"];
	[presence setTo: subscriber];
	[_connection sendStanza: presence];
}

- (void)addDelegate: (id <XMPPContactManagerDelegate>)delegate
{
	[_delegates addDelegate: delegate];
}

- (void)removeDelegate: (id <XMPPContactManagerDelegate>)delegate
{
	[_delegates removeDelegate: delegate];
}

- (void)rosterWasReceived: (XMPPRoster*)roster
- (void)rosterWasReceived: (XMPPRoster *)roster
{
	OFEnumerator *contactEnumerator;
	XMPPContact *contact;
	OFDictionary *rosterItems;
	OFEnumerator *rosterItemEnumerator;
	OFString *bareJID;

116
117
118
119
120
121
122
123
124


125
126
127
128
129
130
131
117
118
119
120
121
122
123


124
125
126
127
128
129
130
131
132







-
-
+
+







		[_delegates broadcastSelector: @selector(contactManager:
						   didAddContact:)
				   withObject: self
				   withObject: contact];
	}
}

-         (void)roster: (XMPPRoster*)roster
  didReceiveRosterItem: (XMPPRosterItem*)rosterItem
-         (void)roster: (XMPPRoster *)roster
  didReceiveRosterItem: (XMPPRosterItem *)rosterItem
{
	XMPPContact *contact;
	OFString *bareJID = [[rosterItem JID] bareJID];

	contact = [_contacts objectForKey: bareJID];

	if ([[rosterItem subscription] isEqual: @"remove"]) {
152
153
154
155
156
157
158
159
160


161
162
163
164
165
166
167
153
154
155
156
157
158
159


160
161
162
163
164
165
166
167
168







-
-
+
+







						   willUpdateWithRosterItem:)
				   withObject: contact
				   withObject: rosterItem];
		[contact XMPP_setRosterItem: rosterItem];
	}
}

-   (void)connection: (XMPPConnection*)connection
  didReceivePresence: (XMPPPresence*)presence
-   (void)connection: (XMPPConnection *)connection
  didReceivePresence: (XMPPPresence *)presence
{
	XMPPContact *contact;
	XMPPJID *JID = [presence from];
	OFString *type = [presence type];

	/* Subscription request */
	if ([type isEqual: @"subscribe"]) {
195
196
197
198
199
200
201
202
203


204
205
206
207
208
209
210
196
197
198
199
200
201
202


203
204
205
206
207
208
209
210
211







-
-
+
+







						   didSendPresence:)
				   withObject: contact
				   withObject: presence];
		return;
	}
}

-  (void)connection: (XMPPConnection*)connection
  didReceiveMessage: (XMPPMessage*)message
-  (void)connection: (XMPPConnection *)connection
  didReceiveMessage: (XMPPMessage *)message
{
	XMPPJID *JID = [message from];
	XMPPContact *contact = [_contacts objectForKey: [JID bareJID]];

	if (contact == nil)
		return;

Modified src/XMPPDiscoEntity.h from [675c041e84] to [1a213e145c].

22
23
24
25
26
27
28


29
30
31
32
33
34
35
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37







+
+







 */

#import <ObjFW/ObjFW.h>

#import "XMPPConnection.h"
#import "XMPPDiscoNode.h"

OF_ASSUME_NONNULL_BEGIN

@class XMPPJID;

/**
 * \brief A class representing an entity responding to Service Discovery
 *	  queries
 */
@interface XMPPDiscoEntity: XMPPDiscoNode <XMPPConnectionDelegate>
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
102
103

104
105
106
107
108
109
110

111


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
102
103
104
105
106
107
108


109
110
111
112
113
114
115
116

117
118
119
120
121
122
123

124
125
126
127







+
+
+
+
+
+







-
+









-
-
+
+
+
+
+
+
+
+









-
+










-
-
+
+






-
+






-
+

+
+
@property (readonly) OFDictionary *discoNodes;

/**
 * The node advertised for the entity's capabilites.
 */
@property (readonly) OFString *capsNode;

+ (instancetype)discoNodeWithJID: (XMPPJID *)JID
			    node: (nullable OFString *)node OF_UNAVAILABLE;
+ (instancetype)discoNodeWithJID: (XMPPJID *)JID
			    node: (nullable OFString *)node
			    name: (nullable OFString *)name OF_UNAVAILABLE;

/**
 * \brief Creates a new autoreleased XMPPDiscoEntity with the specified
 *	  connection.
 *
 * \param connection The XMPPConnection to serve responses on.
 * \return A new autoreleased XMPPDiscoEntity
 */
+ (instancetype)discoEntityWithConnection: (XMPPConnection*)connection;
+ (instancetype)discoEntityWithConnection: (XMPPConnection *)connection;

/**
 * \brief Creates a new autoreleased XMPPDiscoEntity with the specified
 *	  connection.
 *
 * \param connection The XMPPConnection to serve responses on.
 * \param capsNode The node advertised for the entity's capabilites
 * \return A new autoreleased XMPPDiscoEntity
 */
+ (instancetype)discoEntityWithConnection: (XMPPConnection*)connection
				 capsNode: (OFString*)capsNode;
+ (instancetype)discoEntityWithConnection: (XMPPConnection *)connection
				 capsNode: (OFString *)capsNode;

- initWithJID: (XMPPJID *)JID
	 node: (nullable OFString *)node OF_UNAVAILABLE;
- initWithJID: (XMPPJID *)JID
	 node: (nullable OFString *)node
	 name: (nullable OFString *)name OF_UNAVAILABLE;

/**
 * \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;
- initWithConnection: (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)
 * \param capsNode The node advertised for the entity's capabilites
 * \return An initialized XMPPDiscoEntity
 */
- initWithConnection: (XMPPConnection*)connection
	    capsNode: (OFString*)capsNode;
- initWithConnection: (XMPPConnection *)connection
	    capsNode: (nullable OFString *)capsNode OF_DESIGNATED_INITIALIZER;

/**
 * \brief Adds a XMPPDiscoNode to provide responses for.
 *
 * \param node The XMPPDiscoNode to provide responses for
 */
- (void)addDiscoNode: (XMPPDiscoNode*)node;
- (void)addDiscoNode: (XMPPDiscoNode *)node;

/**
 * \brief Calculates the Entity Capabilities Hash of the entity
 *
 * \return A OFString containing the capabilities hash
 */
- (OFString*)capsHash;
- (OFString *)capsHash;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPDiscoEntity.m from [35881ca3b2] to [f708ffe294].

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
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







+
+







-
+




-
-
+
+





-
+





+
+
+
+
+
+
+
-
-
+
+


-
+
+







 * 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 "XMPPDiscoNode.h"
#import "XMPPDiscoNode+Private.h"
#import "XMPPDiscoIdentity.h"
#import "XMPPIQ.h"
#import "namespaces.h"

@implementation XMPPDiscoEntity
@synthesize discoNodes = _discoNodes, capsNode = _capsNode;

+ (instancetype)discoEntityWithConnection: (XMPPConnection*)connection
+ (instancetype)discoEntityWithConnection: (XMPPConnection *)connection
{
	return [[[self alloc] initWithConnection: connection] autorelease];
}

+ (instancetype)discoEntityWithConnection: (XMPPConnection*)connection
				 capsNode: (OFString*)capsNode
+ (instancetype)discoEntityWithConnection: (XMPPConnection *)connection
				 capsNode: (OFString *)capsNode
{
	return [[[self alloc] initWithConnection: connection
					capsNode: capsNode] autorelease];
}

- initWithConnection: (XMPPConnection*)connection
- initWithConnection: (XMPPConnection *)connection
{
	return [self initWithConnection: connection
			       capsNode: nil];
}

- initWithJID: (XMPPJID *)JID
	 node: (nullable OFString *)node
	 name: (nullable OFString *)name
{
	OF_INVALID_INIT_METHOD
}

- initWithConnection: (XMPPConnection*)connection
	    capsNode: (OFString*)capsNode
- initWithConnection: (XMPPConnection *)connection
	    capsNode: (OFString *)capsNode
{
	self = [super initWithJID: [connection JID]
			     node: nil];
			     node: nil
			     name: nil];

	@try {
		_discoNodes = [[OFMutableDictionary alloc] init];
		_connection = connection;
		_capsNode = [capsNode copy];

		[_connection addDelegate: self];
71
72
73
74
75
76
77
78

79
80
81
82
83
84

85
86
87
88
89
90
91
81
82
83
84
85
86
87

88
89
90
91
92
93

94
95
96
97
98
99
100
101







-
+





-
+







{
	[_connection removeDelegate: self];
	[_discoNodes release];

	[super dealloc];
}

- (void)addDiscoNode: (XMPPDiscoNode*)node
- (void)addDiscoNode: (XMPPDiscoNode *)node
{
	[_discoNodes setObject: node
			forKey: [node node]];
}

- (OFString*)capsHash
- (OFString *)capsHash
{
	OFEnumerator *enumerator;
	XMPPDiscoIdentity *identity;
	OFString *feature;
	OFMutableString *caps = [OFMutableString string];
	OFSHA1Hash *hash = [OFSHA1Hash cryptoHash];
	OFDataArray *digest = [OFDataArray dataArray];
104
105
106
107
108
109
110
111
112


113
114
115
116
117
118


119
120
121
122
123
124
125
114
115
116
117
118
119
120


121
122
123
124
125
126


127
128
129
130
131
132
133
134
135







-
-
+
+




-
-
+
+








	[digest addItems: [hash digest]
		   count: [OFSHA1Hash digestSize]];

	return [digest stringByBase64Encoding];
}

- (void)connection: (XMPPConnection*)connection
     wasBoundToJID: (XMPPJID*)JID
- (void)connection: (XMPPConnection *)connection
     wasBoundToJID: (XMPPJID *)JID
{
	_JID = [JID copy];
}

- (bool)connection: (XMPPConnection*)connection
      didReceiveIQ: (XMPPIQ*)IQ
- (bool)connection: (XMPPConnection *)connection
      didReceiveIQ: (XMPPIQ *)IQ
{
	if (![[IQ to] isEqual: _JID])
		return false;

	OFXMLElement *query = [IQ elementForName: @"query"
				       namespace: XMPP_NS_DISCO_ITEMS];

Modified src/XMPPDiscoIdentity.h from [574d3c2fee] to [969a73bffe].

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


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







+
+









-
+

-
+

-
+










-
-
-
+
+
+









-
-
+
+
+
+










-
-
-
+
+
+









-
-
+
+

+
+
 * 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 <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

/**
 * \brief A class describing a Service Discovery Identity
 */
@interface XMPPDiscoIdentity: OFObject <OFComparing>
{
	OFString *_category, *_name, *_type;
}

/// \brief The category of the identity
@property (readonly) OFString *category;
@property (readonly, nonatomic) OFString *category;
/// \brief The name of the identity, might be unset
@property (readonly) OFString *name;
@property (readonly, nonatomic) OFString *name;
/// \brief The type of the identity
@property (readonly) OFString *type;
@property (readonly, nonatomic) OFString *type;

/**
 * \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
 */
+ (instancetype)identityWithCategory: (OFString*)category
				type: (OFString*)type
				name: (OFString*)name;
+ (instancetype)identityWithCategory: (OFString *)category
				type: (OFString *)type
				name: (nullable 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
 */
+ (instancetype)identityWithCategory: (OFString*)category
				type: (OFString*)type;
+ (instancetype)identityWithCategory: (OFString *)category
				type: (OFString *)type;

- init OF_UNAVAILABLE;

/**
 * \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;
- initWithCategory: (OFString *)category
	      type: (OFString *)type
	      name: (nullable OFString *)name OF_DESIGNATED_INITIALIZER;

/**
 * \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;
- initWithCategory: (OFString *)category
	      type: (OFString *)type;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPDiscoIdentity.m from [95bd037e63] to [52a90e08e0].

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
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







-
-
-
+
+
+






-
-
+
+





-
-
-
+
+
+


















-
-
+
+








-
-
-
-
-
-
-
+
-







 */

#import "XMPPDiscoIdentity.h"

@implementation XMPPDiscoIdentity
@synthesize category = _category, name = _name, type = _type;

+ (instancetype)identityWithCategory: (OFString*)category
				type: (OFString*)type
				name: (OFString*)name
+ (instancetype)identityWithCategory: (OFString *)category
				type: (OFString *)type
				name: (OFString *)name
{
	return [[[self alloc] initWithCategory: category
					  type: type
					  name: name] autorelease];
}

+ (instancetype)identityWithCategory: (OFString*)category
				type: (OFString*)type
+ (instancetype)identityWithCategory: (OFString *)category
				type: (OFString *)type
{
	return [[[self alloc] initWithCategory: category
					  type: type] autorelease];
}

- initWithCategory: (OFString*)category
	      type: (OFString*)type
	      name: (OFString*)name
- initWithCategory: (OFString *)category
	      type: (OFString *)type
	      name: (OFString *)name
{
	self = [super init];

	@try {
		if (category == nil || type == nil)
			@throw [OFInvalidArgumentException exception];

		_category = [category copy];
		_name = [name copy];
		_type = [type copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- initWithCategory: (OFString*)category
	      type: (OFString*)type
- 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;
	}

	OF_INVALID_INIT_METHOD
	abort();
}

- (void)dealloc
{
	[_category release];
	[_name release];
	[_type release];
135
136
137
138
139
140
141
142

143
144
145
146
147
148
149
150
151
152
153
154
128
129
130
131
132
133
134

135
136
137
138
139
140
141
142
143
144
145
146
147







-
+













	if (object == self)
		return OF_ORDERED_SAME;

	if (![object isKindOfClass: [XMPPDiscoIdentity class]])
		@throw [OFInvalidArgumentException exception];

	identity = (XMPPDiscoIdentity*)object;
	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+Private.h version [e20421d02a].
















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#import "XMPPDiscoNode.h"

OF_ASSUME_NONNULL_BEGIN

@class XMPPConnection;
@class XMPPIQ;

@interface XMPPDiscoNode ()
- (bool)XMPP_handleItemsIQ: (XMPPIQ *)IQ
		connection: (XMPPConnection *)connection;
- (bool)XMPP_handleInfoIQ: (XMPPIQ *)IQ
	       connection: (XMPPConnection *)connection;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPDiscoNode.h from [fa28e0da2b] to [04990bb259].

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



102
103
104
105
106
107
108

109
110
111
112
113
114
115

116
117
118
119
120
121
122


123
124
125

126
127
128
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
102
103
104
105
106
107
108
109

110
111
112
113
114
115
116

117
118
119
120
121
122
123

124
125
126


127










+
+


















-
+

-
+

-
+

-
+

-
+

-
+









-
-
+
+










-
-
-
+
+
+









-
-
+
+










-
-
-
+
+
+






-
+






-
+






-
+
+

-
-
+
-
-
-
 * 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 <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@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;
}

/// \brief The JID this node lives on
@property (readonly) XMPPJID *JID;
@property (readonly, nonatomic) XMPPJID *JID;
/// \brief The node's opaque name of the node
@property (readonly) OFString *node;
@property (readonly, nonatomic) OFString *node;
/// \brief The node's human friendly name (may be unspecified)
@property (readonly) OFString *name;
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *name;
/// \brief The node's list of identities
@property (readonly) OFSortedList *identities;
@property (readonly, nonatomic) OFSortedList *identities;
/// \brief The node's list of features
@property (readonly) OFSortedList *features;
@property (readonly, nonatomic) OFSortedList *features;
/// \brief The node's children
@property (readonly) OFDictionary *childNodes;
@property (readonly, nonatomic) OFDictionary *childNodes;

/**
 * \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
 */
+ (instancetype)discoNodeWithJID: (XMPPJID*)JID
			    node: (OFString*)node;
+ (instancetype)discoNodeWithJID: (XMPPJID *)JID
			    node: (nullable 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
 */
+ (instancetype)discoNodeWithJID: (XMPPJID*)JID
			    node: (OFString*)node
			    name: (OFString*)name;
+ (instancetype)discoNodeWithJID: (XMPPJID *)JID
			    node: (nullable OFString *)node
			    name: (nullable 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;
- initWithJID: (XMPPJID *)JID
	 node: (nullable 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;
- initWithJID: (XMPPJID *)JID
	 node: (nullable OFString *)node
	 name: (nullable OFString *)name OF_DESIGNATED_INITIALIZER;

 /**
  * \brief Adds an XMPPDiscoIdentity to the node
  *
  * \param identity The XMPPDiscoIdentity to add
  */
- (void)addIdentity: (XMPPDiscoIdentity*)identity;
- (void)addIdentity: (XMPPDiscoIdentity *)identity;

 /**
  * \brief Adds a feature to the node
  *
  * \param feature The feature to add
  */
- (void)addFeature: (OFString*)feature;
- (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;
- (void)addChildNode: (XMPPDiscoNode *)node;
@end

- (bool)XMPP_handleItemsIQ: (XMPPIQ*)IQ
		connection: (XMPPConnection*)connection;
OF_ASSUME_NONNULL_END
- (bool)XMPP_handleInfoIQ: (XMPPIQ*)IQ
	       connection: (XMPPConnection*)connection;
@end

Modified src/XMPPDiscoNode.m from [adea212c4a] to [07959d2558].

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
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







+
+




-








-
-
+
+





-
-
-
+
+
+






-
-
+
+






-
-
-
+
+
+







 * 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 "XMPPDiscoNode.h"
#import "XMPPDiscoNode+Private.h"
#import "XMPPConnection.h"
#import "XMPPIQ.h"
#import "XMPPJID.h"
#import "XMPPDiscoEntity.h"
#import "XMPPDiscoNode.h"
#import "XMPPDiscoIdentity.h"
#import "namespaces.h"

@implementation XMPPDiscoNode

@synthesize JID = _JID, node = _node, name = _name, identities = _identities;
@synthesize features = _features, childNodes = _childNodes;

+ (instancetype)discoNodeWithJID: (XMPPJID*)JID
			    node: (OFString*)node;
+ (instancetype)discoNodeWithJID: (XMPPJID *)JID
			    node: (OFString *)node;
{
	return [[[self alloc] initWithJID: JID
				     node: node] autorelease];
}

+ (instancetype)discoNodeWithJID: (XMPPJID*)JID
			    node: (OFString*)node
			    name: (OFString*)name
+ (instancetype)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
- initWithJID: (XMPPJID *)JID
	 node: (OFString *)node
{
	return [self initWithJID: JID
			    node: node
			    name: nil];
}

- initWithJID: (XMPPJID*)JID
	 node: (OFString*)node
	 name: (OFString*)name
- initWithJID: (XMPPJID *)JID
	 node: (OFString *)node
	 name: (OFString *)name
{
	self = [super init];

	@try {
		if (JID == nil &&
		    ![self isKindOfClass: [XMPPDiscoEntity class]])
			@throw [OFInvalidArgumentException exception];
94
95
96
97
98
99
100





101

102
103
104
105
106

107
108
109
110
111

112
113
114
115
116
117
118


119
120
121
122
123
124
125
95
96
97
98
99
100
101
102
103
104
105
106

107
108
109
110
111

112
113
114
115
116

117
118
119
120
121
122


123
124
125
126
127
128
129
130
131







+
+
+
+
+
-
+




-
+




-
+





-
-
+
+







	[_identities release];
	[_features release];
	[_childNodes release];

	[super dealloc];
}

- (OFDictionary *)childNodes
{
	return [[_childNodes copy] autorelease];
}

- (void)addIdentity: (XMPPDiscoIdentity*)identity
- (void)addIdentity: (XMPPDiscoIdentity *)identity
{
	[_identities insertObject: identity];
}

- (void)addFeature: (OFString*)feature
- (void)addFeature: (OFString *)feature
{
	[_features insertObject: feature];
}

- (void)addChildNode: (XMPPDiscoNode*)node
- (void)addChildNode: (XMPPDiscoNode *)node
{
	[_childNodes setObject: node
			forKey: [node node]];
}

- (bool)XMPP_handleItemsIQ: (XMPPIQ*)IQ
		connection: (XMPPConnection*)connection
- (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];
152
153
154
155
156
157
158
159
160


161
162
163
164
165
166
167
158
159
160
161
162
163
164


165
166
167
168
169
170
171
172
173







-
-
+
+







	}

	[connection sendStanza: resultIQ];

	return true;
}

- (bool)XMPP_handleInfoIQ: (XMPPIQ*)IQ
	       connection: (XMPPConnection*)connection
- (bool)XMPP_handleInfoIQ: (XMPPIQ *)IQ
	       connection: (XMPPConnection *)connection
{
	XMPPIQ *resultIQ;
	OFXMLElement *response;
	OFEnumerator *enumerator;
	OFString *feature;
	XMPPDiscoIdentity *identity;

Modified src/XMPPEXTERNALAuth.h from [87300c70b8] to [0051e84d45].

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


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







+
+

















-
+

+
+
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#import <ObjFW/ObjFW.h>
#import "XMPPAuthenticator.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * \brief A class to authenticate using SASL EXTERNAL
 */
@interface XMPPEXTERNALAuth: XMPPAuthenticator
/**
 * \brief Creates a new autoreleased XMPPEXTERNALAuth.
 *
 * \return A new autoreleased XMPPEXTERNALAuth
 */
+ (instancetype)EXTERNALAuth;

/**
 * \brief Creates a new autoreleased XMPPEXTERNALAuth with an authzid.
 *
 * \param authzid The authzid to get authorization for
 * \return A new autoreleased XMPPEXTERNALAuth
 */
+ (instancetype)EXTERNALAuthWithAuthzid: (OFString*)authzid;
+ (instancetype)EXTERNALAuthWithAuthzid: (nullable OFString *)authzid;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPEXTERNALAuth.m from [4c3013d555] to [c6103d3079].

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
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







-
+






-
+










@implementation XMPPEXTERNALAuth: XMPPAuthenticator
+ (instancetype)EXTERNALAuth
{
	return [[[self alloc] initWithAuthcid: nil
				     password: nil] autorelease];
}

+ (instancetype)EXTERNALAuthWithAuthzid: (OFString*)authzid
+ (instancetype)EXTERNALAuthWithAuthzid: (OFString *)authzid
{
	return [[[self alloc] initWithAuthzid: authzid
				      authcid: nil
				     password: nil] autorelease];
}

- (OFDataArray*)initialMessage
- (OFDataArray *)initialMessage
{
	OFDataArray *message = [OFDataArray dataArray];

	/* authzid */
	if (_authzid)
		[message addItem: _authzid];

	return message;
}
@end

Modified src/XMPPExceptions.h from [69b290104a] to [679b6851a0].

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
102
103
104
105
106
107

108
109

110
111
112
113
114
115
116
117
118
119
120
121





122
123
124
125
126
127
128
129
130
131
132
133



134
135
136
137
138
139
140
141
142
143
144
145

146
147

148
149
150
151
152
153
154
155
156
157
158
159





160
161
162
163
164
165
166
167
168
169
170
171



172
173
174
175
176
177
178
179
180
181
182
183

184
185
186
187
188
189
190
191
192
193




194
195
196
197
198
199
200
201
202
203


204


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
102
103
104
105
106
107
108
109
110
111
112
113

114
115

116
117
118
119
120
121
122
123
124
125



126
127
128
129
130
131
132
133
134
135
136
137
138
139



140
141
142
143
144
145
146
147
148
149
150
151
152
153

154
155

156
157
158
159
160
161
162
163
164
165



166
167
168
169
170
171
172
173
174
175
176
177
178
179



180
181
182
183
184
185
186
187
188
189
190
191
192
193

194
195
196
197
198
199
200
201
202


203
204
205
206
207
208
209
210
211
212
213
214


215
216
217
218
219







+
+












-
+








-
+
+
+








-
+
+











-
+

-
+









-
-
-
+
+
+
+
+









-
-
-
+
+
+












-
+

-
+









-
-
-
+
+
+
+
+









-
-
-
+
+
+











-
+

-
+









-
-
-
+
+
+
+
+









-
-
-
+
+
+











-
+








-
-
+
+
+
+








-
-
+
+

+
+
 * 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 <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@class XMPPConnection;
@class XMPPAuthenticator;

/**
 * \brief A base class for XMPP related exceptions
 */
@interface XMPPException: OFException
{
	XMPPConnection *_connection;
}

/// \brief The connection the exception relates to
@property (readonly, retain) XMPPConnection *connection;
@property OF_NULLABLE_PROPERTY (readonly, nonatomic) XMPPConnection *connection;

/**
 * \brief Creates a new XMPPException.
 *
 * \param connection The connection that received the data responsible
 *	  for this exception
 * \return A new XMPPException
 */
+ (instancetype)exceptionWithConnection: (XMPPConnection*)connection;
+ (instancetype)exceptionWithConnection: (nullable XMPPConnection *)connection;

- init OF_UNAVAILABLE;

/**
 * \brief Initializes an already allocated XMPPException.
 *
 * \param connection The connection that received the data responsible
 *	  for this exception
 * \return An initialized XMPPException
 */
- initWithConnection: (XMPPConnection*)connection;
- initWithConnection: (nullable XMPPConnection *)connection
    OF_DESIGNATED_INITIALIZER;
@end

/**
 * \brief An exception indicating a stream error was received
 */
@interface XMPPStreamErrorException: XMPPException
{
	OFString *_condition, *_reason;
}

/// \brief The defined error condition specified by the stream error
@property (readonly, copy) OFString *condition;
@property (readonly, nonatomic) OFString *condition;
/// \brief The descriptive free-form text specified by the stream error
@property (readonly, copy) OFString *reason;
@property (readonly, nonatomic) OFString *reason;

/**
 * \brief Creates a new XMPPStreamErrorException.
 *
 * \param connection The connection that received the stream error
 * \param condition The defined error condition specified by the stream error
 * \param reason The descriptive free-form text specified by the stream error
 * \return A new XMPPStreamErrorException
 */
+ (instancetype)exceptionWithConnection: (XMPPConnection*)connection
			      condition: (OFString*)condition
				 reason: (OFString*)reason;
+ (instancetype)exceptionWithConnection: (nullable XMPPConnection *)connection
			      condition: (OFString *)condition
				 reason: (OFString *)reason;

- initWithConnection: (nullable XMPPConnection *)connection OF_UNAVAILABLE;

/**
 * \brief Initializes an already allocated XMPPStreamErrorException.
 *
 * \param connection The connection that received the stream error
 * \param condition The defined error condition specified by the stream error
 * \param reason The descriptive free-form text specified by the stream error
 * \return An initialized XMPPStreamErrorException
 */
- initWithConnection: (XMPPConnection*)connection
	   condition: (OFString*)condition
	      reason: (OFString*)reason;
- initWithConnection: (nullable XMPPConnection *)connection
	   condition: (OFString *)condition
	      reason: (OFString *)reason OF_DESIGNATED_INITIALIZER;
@end

/**
 * \brief An exception indicating a stringprep profile
 *	  did not apply to a string
 */
@interface XMPPStringPrepFailedException: XMPPException
{
	OFString *_profile, *_string;
}

/// \brief The name of the stringprep profile that did not apply
@property (readonly, copy) OFString *profile;
@property (readonly, nonatomic) OFString *profile;
/// \brief The string that failed the stringprep profile
@property (readonly, copy) OFString *string;
@property (readonly, nonatomic) OFString *string;

/**
 * \brief Creates a new XMPPStringPrepFailedException.
 *
 * \param connection The connection the string relates to
 * \param profile The name of the stringprep profile that did not apply
 * \param string The string that failed the stringprep profile
 * \return A new XMPPStringPrepFailedException
 */
+ (instancetype)exceptionWithConnection: (XMPPConnection*)connection
				profile: (OFString*)profile
				 string: (OFString*)string;
+ (instancetype)exceptionWithConnection: (nullable XMPPConnection *)connection
				profile: (OFString *)profile
				 string: (OFString *)string;

- initWithConnection: (nullable XMPPConnection *)connection OF_UNAVAILABLE;

/**
 * \brief Initializes an already allocated XMPPStringPrepFailedException.
 *
 * \param connection The connection the string relates to
 * \param profile The name of the stringprep profile that did not apply
 * \param string The string that failed the stringprep profile
 * \return An initialized XMPPStringPrepFailedException
 */
- initWithConnection: (XMPPConnection*)connection
	     profile: (OFString*)profile
	      string: (OFString*)string;
- initWithConnection: (nullable XMPPConnection *)connection
	     profile: (OFString *)profile
	      string: (OFString *)string OF_DESIGNATED_INITIALIZER;
@end

/**
 * \brief An exception indicating IDNA translation of a string failed
 */
@interface XMPPIDNATranslationFailedException: XMPPException
{
	OFString *_operation, *_string;
}

/// \brief The IDNA translation operation which failed
@property (readonly, copy) OFString *operation;
@property (readonly, nonatomic) OFString *operation;
/// \brief The string that could not be translated
@property (readonly, copy) OFString *string;
@property (readonly, nonatomic) OFString *string;

/**
 * \brief Creates a new XMPPIDNATranslationFailedException.
 *
 * \param connection The connection the string relates to
 * \param operation The name of the stringprep profile that did not apply
 * \param string The string that could not be translated
 * \return A new XMPPIDNATranslationFailedException
 */
+ (instancetype)exceptionWithConnection: (XMPPConnection*)connection
			      operation: (OFString*)operation
				 string: (OFString*)string;
+ (instancetype)exceptionWithConnection: (nullable XMPPConnection *)connection
			      operation: (OFString *)operation
				 string: (OFString *)string;

- initWithConnection: (nullable XMPPConnection *)connection OF_UNAVAILABLE;

/**
 * \brief Initializes an already allocated XMPPIDNATranslationFailedException.
 *
 * \param connection The connection the string relates to
 * \param operation The name of the stringprep profile that did not apply
 * \param string The string that could not be translated
 * \return An initialized XMPPIDNATranslationFailedException
 */
- initWithConnection: (XMPPConnection*)connection
	   operation: (OFString*)operation
	      string: (OFString*)string;
- initWithConnection: (nullable XMPPConnection *)connection
	   operation: (OFString *)operation
	      string: (OFString *)string;
@end

/**
 * \brief An exception indicating authentication failed
 */
@interface XMPPAuthFailedException: XMPPException
{
	OFString *_reason;
}

/// \brief The reason the authentication failed
@property (readonly, copy) OFString *reason;
@property (readonly, nonatomic) OFString *reason;

/**
 * \brief Creates a new XMPPAuthFailedException.
 *
 * \param connection The connection that could not be authenticated
 * \param reason The reason the authentication failed
 * \return A new XMPPAuthFailedException
 */
+ (instancetype)exceptionWithConnection: (XMPPConnection*)connection
				 reason: (OFString*)reason;
+ (instancetype)exceptionWithConnection: (nullable XMPPConnection *)connection
				 reason: (OFString *)reason;

- initWithConnection: (nullable XMPPConnection *)connection OF_UNAVAILABLE;

/**
 * \brief Initializes an already allocated XMPPAuthFailedException.
 *
 * \param connection The connection that could not be authenticated
 * \param reason The reason the authentication failed
 * \return An initialized XMPPAuthFailedException
 */
- initWithConnection: (XMPPConnection*)connection
	      reason: (OFString*)reason;
- initWithConnection: (nullable XMPPConnection *)connection
	      reason: (OFString *)reason OF_DESIGNATED_INITIALIZER;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPExceptions.m from [d64ce8e761] to [b482b72e53].

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
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







-
+









-
+








#import "XMPPExceptions.h"
#import "XMPPConnection.h"

@implementation XMPPException
@synthesize connection = _connection;

+ (instancetype)exceptionWithConnection: (XMPPConnection*)connection
+ (instancetype)exceptionWithConnection: (XMPPConnection *)connection
{
	return [[[self alloc] initWithConnection: connection] autorelease];
}

- init
{
	OF_INVALID_INIT_METHOD
}

- initWithConnection: (XMPPConnection*)connection
- initWithConnection: (XMPPConnection *)connection
{
	self = [super init];

	@try {
		_connection = [connection retain];
	} @catch (id e) {
		[self release];
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
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







-
-
-
+
+
+






-
+




-
-
-
+
+
+







	[super dealloc];
}
@end

@implementation XMPPStreamErrorException
@synthesize condition = _condition, reason = _reason;

+ (instancetype)exceptionWithConnection: (XMPPConnection*)connection
			      condition: (OFString*)condition
				 reason: (OFString*)reason;
+ (instancetype)exceptionWithConnection: (XMPPConnection *)connection
			      condition: (OFString *)condition
				 reason: (OFString *)reason;
{
	return [[[self alloc] initWithConnection: connection
				       condition: condition
					  reason: reason] autorelease];
}

- initWithConnection: (XMPPConnection*)connection
- initWithConnection: (XMPPConnection *)connection
{
	OF_INVALID_INIT_METHOD
}

- initWithConnection: (XMPPConnection*)connection
	   condition: (OFString*)condition
	      reason: (OFString*)reason
- initWithConnection: (XMPPConnection *)connection
	   condition: (OFString *)condition
	      reason: (OFString *)reason
{
	self = [super initWithConnection: connection];

	@try {
		_condition = [condition copy];
		_reason = [reason copy];
	} @catch (id e) {
103
104
105
106
107
108
109
110

111
112
113
114
115
116
117
118
119
120
121
122



123
124
125
126
127
128
129

130
131
132
133
134
135
136



137
138
139
140
141
142
143
103
104
105
106
107
108
109

110
111
112
113
114
115
116
117
118
119



120
121
122
123
124
125
126
127
128

129
130
131
132
133



134
135
136
137
138
139
140
141
142
143







-
+









-
-
-
+
+
+






-
+




-
-
-
+
+
+







{
	[_condition release];
	[_reason release];

	[super dealloc];
}

- (OFString*)description
- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Got stream error: %@. Reason: %@!", _condition, _reason];
}
@end

@implementation XMPPStringPrepFailedException
@synthesize profile = _profile, string = _string;

+ (instancetype)exceptionWithConnection: (XMPPConnection*)connection
				profile: (OFString*)profile
				 string: (OFString*)string
+ (instancetype)exceptionWithConnection: (XMPPConnection *)connection
				profile: (OFString *)profile
				 string: (OFString *)string
{
	return [[[self alloc] initWithConnection: connection
					 profile: profile
					  string: string] autorelease];
}

- initWithConnection: (XMPPConnection*)connection
- initWithConnection: (XMPPConnection *)connection
{
	OF_INVALID_INIT_METHOD
}

- initWithConnection: (XMPPConnection*)connection
	     profile: (OFString*)profile
	      string: (OFString*)string
- initWithConnection: (XMPPConnection *)connection
	     profile: (OFString *)profile
	      string: (OFString *)string
{
	self = [super initWithConnection: connection];

	@try {
		_profile = [profile copy];
		_string = [string copy];
	} @catch (id e) {
152
153
154
155
156
157
158
159

160
161
162
163
164
165
166
167
168
169
170
171
172



173
174
175
176
177
178
179

180
181
182
183
184
185
186



187
188
189
190
191
192
193
152
153
154
155
156
157
158

159
160
161
162
163
164
165
166
167
168
169



170
171
172
173
174
175
176
177
178

179
180
181
182
183



184
185
186
187
188
189
190
191
192
193







-
+










-
-
-
+
+
+






-
+




-
-
-
+
+
+







{
	[_profile release];
	[_string release];

	[super dealloc];
}

- (OFString*)description
- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Stringprep with profile %@ failed on string '%@'!",
	    _profile, _string];
}
@end

@implementation XMPPIDNATranslationFailedException
@synthesize operation = _operation, string = _string;

+ (instancetype)exceptionWithConnection: (XMPPConnection*)connection
			      operation: (OFString*)operation
				 string: (OFString*)string
+ (instancetype)exceptionWithConnection: (XMPPConnection *)connection
			      operation: (OFString *)operation
				 string: (OFString *)string
{
	return [[[self alloc] initWithConnection: connection
				       operation: operation
					  string: string] autorelease];
}

- initWithConnection: (XMPPConnection*)connection
- initWithConnection: (XMPPConnection *)connection
{
	OF_INVALID_INIT_METHOD
}

- initWithConnection: (XMPPConnection*)connection
	   operation: (OFString*)operation
	      string: (OFString*)string
- initWithConnection: (XMPPConnection *)connection
	   operation: (OFString *)operation
	      string: (OFString *)string
{
	self = [super initWithConnection: connection];

	@try {
		_operation = [operation copy];
		_string = [string copy];
	} @catch (id e) {
202
203
204
205
206
207
208
209

210
211
212
213
214
215
216
217
218
219
220


221
222
223
224
225
226

227
228
229
230
231
232


233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253

254
255
256
257
258
202
203
204
205
206
207
208

209
210
211
212
213
214
215
216
217
218


219
220
221
222
223
224
225

226
227
228
229
230


231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252

253
254
255
256
257
258







-
+









-
-
+
+





-
+




-
-
+
+




















-
+





{
	[_operation release];
	[_string release];

	[super dealloc];
}

- (OFString*)description
- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"IDNA operation %@ failed on string '%@'!", _operation, _string];
}
@end

@implementation XMPPAuthFailedException
@synthesize reason = _reason;

+ (instancetype)exceptionWithConnection: (XMPPConnection*)connection
				 reason: (OFString*)reason;
+ (instancetype)exceptionWithConnection: (XMPPConnection *)connection
				 reason: (OFString *)reason;
{
	return [[[self alloc] initWithConnection: connection
					  reason: reason] autorelease];
}

- initWithConnection: (XMPPConnection*)connection
- initWithConnection: (XMPPConnection *)connection
{
	OF_INVALID_INIT_METHOD
}

- initWithConnection: (XMPPConnection*)connection
	      reason: (OFString*)reason
- initWithConnection: (XMPPConnection *)connection
	      reason: (OFString *)reason
{
	self = [super initWithConnection: connection];

	@try {
		_reason = [reason copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_reason release];

	[super dealloc];
}

- (OFString*)description
- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Authentication failed. Reason: %@!", _reason];
}
@end

Modified src/XMPPFileStorage.h from [8262bc5c51] to [7e93a8c354].

19
20
21
22
23
24
25


26
27
28
29
30
31
32
33
34

35

36


19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

38
39
40
41







+
+









+
-
+

+
+
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#import <ObjFW/OFObject.h>

#import "XMPPStorage.h"

OF_ASSUME_NONNULL_BEGIN

@class OFMutableDictionary;

@interface XMPPFileStorage: OFObject <XMPPStorage>
{
	OFString *_file;
	OFMutableDictionary *_data;
}

- init OF_UNAVAILABLE;
- initWithFile: (OFString*)file;
- initWithFile: (OFString *)file;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPFileStorage.m from [522ef88dbb] to [d3dafb0daf].

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
36
37
38
39
40
41
42







43

44
45

46
47
48
49
50
51
52
53







-
-
-
-
-
-
-
+
-


-
+







#import <ObjFW/OFNotImplementedException.h>

#import "XMPPFileStorage.h"

@implementation XMPPFileStorage
- init
{
	@try {
		[self doesNotRecognizeSelector: _cmd];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	OF_INVALID_INIT_METHOD
	abort();
}

- initWithFile: (OFString*)file
- initWithFile: (OFString *)file
{
	self = [super init];

	@try {
		OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];

		_file = [file copy];
84
85
86
87
88
89
90
91

92
93
94
95
96
97
98
77
78
79
80
81
82
83

84
85
86
87
88
89
90
91







-
+








- (void)save
{
	[[_data messagePackRepresentation] writeToFile: _file];
}

- (void)XMPP_setObject: (id)object
	       forPath: (OFString*)path
	       forPath: (OFString *)path
{
	OFArray *pathComponents = [path componentsSeparatedByString: @"."];
	OFMutableDictionary *iter = _data;
	OFEnumerator *enumerator = [pathComponents objectEnumerator];
	OFString *component;
	size_t i = 0, components = [pathComponents count];

114
115
116
117
118
119
120
121

122
123
124
125
126
127
128

129
130
131
132
133
134
135


136
137
138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
153
154
155
156
157
158

159
160
161
162
163
164
165
166
167
168

169
170
171
172
173
174
175
176
177
178
179
180
181

182
183
184
185
186
187
188
189
190
191

192
193
194
195
196
197
198
199
200
201
202
203
204


205
206
207
208
209
210
211
212
213
214

215
216
217
218
219
220
221
222
223
224
225
226
227


228
229
230
231
232
233
234
235
236
237

238
239
240
241
242
243
244
245
246
247
248
107
108
109
110
111
112
113

114
115



116
117

118
119
120
121
122
123


124
125
126
127
128
129
130
131
132
133
134

135
136
137
138
139
140
141
142
143
144
145
146
147

148
149
150
151
152
153
154
155
156
157

158
159
160
161
162
163
164
165
166
167
168
169
170

171
172
173
174
175
176
177
178
179
180

181
182
183
184
185
186
187
188
189
190
191
192


193
194
195
196
197
198
199
200
201
202
203

204
205
206
207
208
209
210
211
212
213
214
215


216
217
218
219
220
221
222
223
224
225
226

227
228
229
230
231
232
233
234
235
236
237
238







-
+

-
-
-


-
+





-
-
+
+









-
+












-
+









-
+












-
+









-
+











-
-
+
+









-
+











-
-
+
+









-
+











	if (object != nil)
		[iter setObject: object
			 forKey: [pathComponents lastObject]];
	else
		[iter removeObjectForKey: [pathComponents lastObject]];
}

- (id)XMPP_objectForPath: (OFString*)path
- (id)XMPP_objectForPath: (OFString *)path
{
	OFArray *pathComponents = [path componentsSeparatedByString: @"."];
	OFEnumerator *enumerator = [pathComponents objectEnumerator];
	OFString *component;
	id object = _data;

	while ((component = [enumerator nextObject]) != nil)
	for (OFString *component in [path componentsSeparatedByString: @"."])
		object = [object objectForKey: component];

	return object;
}

- (void)setStringValue: (OFString*)string
	       forPath: (OFString*)path
- (void)setStringValue: (OFString *)string
	       forPath: (OFString *)path
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];

	[self XMPP_setObject: string
		     forPath: path];

	[pool release];
}

- (OFString*)stringValueForPath: (OFString*)path
- (OFString *)stringValueForPath: (OFString *)path
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	OFString *string;

	string = [self XMPP_objectForPath: path];

	[pool release];

	return string;
}

- (void)setBooleanValue: (bool)boolean
		forPath: (OFString*)path
		forPath: (OFString *)path
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];

	[self XMPP_setObject: [OFNumber numberWithBool: boolean]
		     forPath: path];

	[pool release];
}

- (bool)booleanValueForPath: (OFString*)path
- (bool)booleanValueForPath: (OFString *)path
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	bool boolean;

	boolean = [[self XMPP_objectForPath: path] boolValue];

	[pool release];

	return boolean;
}

- (void)setIntegerValue: (intmax_t)integer
		forPath: (OFString*)path
		forPath: (OFString *)path
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];

	[self XMPP_setObject: [OFNumber numberWithIntMax: integer]
		     forPath: path];

	[pool release];
}

- (intmax_t)integerValueForPath: (OFString*)path
- (intmax_t)integerValueForPath: (OFString *)path
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	intmax_t integer;

	integer = [[self XMPP_objectForPath: path] intMaxValue];

	[pool release];

	return integer;
}

- (void)setArray: (OFArray*)array
	 forPath: (OFString*)path
- (void)setArray: (OFArray *)array
	 forPath: (OFString *)path
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];

	[self XMPP_setObject: array
		     forPath: path];

	[pool release];
}

- (OFArray*)arrayForPath: (OFString*)path
- (OFArray *)arrayForPath: (OFString *)path
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	OFArray *array;

	array = [self XMPP_objectForPath: path];

	[pool release];

	return array;
}

- (void)setDictionary: (OFDictionary*)dictionary
	      forPath: (OFString*)path
- (void)setDictionary: (OFDictionary *)dictionary
	      forPath: (OFString *)path
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];

	[self XMPP_setObject: dictionary
		     forPath: path];

	[pool release];
}

- (OFDictionary*)dictionaryForPath: (OFString*)path
- (OFDictionary *)dictionaryForPath: (OFString *)path
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	OFDictionary *dictionary;

	dictionary = [self XMPP_objectForPath: path];

	[pool release];

	return dictionary;
}
@end

Modified src/XMPPIQ.h from [15421246e4] to [e565b6e506].

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


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







+
+











-
-
+
+









-
-
+
+






-
+









-
-
-
+
+
+








-
-
+
+

+
+
 * 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 "XMPPStanza.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * \brief A class describing an IQ stanza.
 */
@interface XMPPIQ: XMPPStanza
/**
 * \brief Creates a new XMPPIQ with the specified type and ID.
 *
 * \param type The value for the stanza's type attribute
 * \param ID The value for the stanza's id attribute
 * \return A new autoreleased XMPPIQ
 */
+ (instancetype)IQWithType: (OFString*)type
			ID: (OFString*)ID;
+ (instancetype)IQWithType: (OFString *)type
			ID: (OFString *)ID;

/**
 * \brief Initializes an already allocated XMPPIQ with the specified type and
 *	  ID.
 *
 * \param type The value for the stanza's type attribute
 * \param ID The value for the stanza's id attribute
 * \return An initialized XMPPIQ
 */
- initWithType: (OFString*)type
	    ID: (OFString*)ID;
- initWithType: (OFString *)type
	    ID: (OFString *)ID;

/**
 * \brief Generates a result IQ for the receiving object.
 *
 * \return A new autoreleased XMPPIQ
 */
- (XMPPIQ*)resultIQ;
- (XMPPIQ *)resultIQ;

/**
 * \brief Generates an error IQ for the receiving object.
 *
 * \param type An error type as defined by RFC 6120
 * \param condition An error condition as defined by RFC 6120
 * \param text A descriptive text
 * \return A new autoreleased XMPPIQ
 */
- (XMPPIQ*)errorIQWithType: (OFString*)type
		 condition: (OFString*)condition
		      text: (OFString*)text;
- (XMPPIQ *)errorIQWithType: (OFString *)type
		  condition: (OFString *)condition
		       text: (nullable OFString *)text;

/**
 * \brief Generates an error IQ for the receiving object.
 *
 * \param type An error type as defined by RFC 6120
 * \param condition A defined conditions from RFC 6120
 * \return A new autoreleased XMPPIQ
 */
- (XMPPIQ*)errorIQWithType: (OFString*)type
		 condition: (OFString*)condition;
- (XMPPIQ *)errorIQWithType: (OFString *)type
		  condition: (OFString *)condition;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPIQ.m from [a625c5453d] to [4202e71e12].

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
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







-
-
+
+





-
-
+
+

















-
+








-
-
-
+
+
+







# include "config.h"
#endif

#import "namespaces.h"
#import "XMPPIQ.h"

@implementation XMPPIQ
+ (instancetype)IQWithType: (OFString*)type
			ID: (OFString*)ID
+ (instancetype)IQWithType: (OFString *)type
			ID: (OFString *)ID
{
	return [[[self alloc] initWithType: type
					ID: ID] autorelease];
}

- initWithType: (OFString*)type
	    ID: (OFString*)ID
- initWithType: (OFString *)type
	    ID: (OFString *)ID
{
	self = [super initWithName: @"iq"
			      type: type
				ID: ID];

	@try {
		if (![type isEqual: @"get"] && ![type isEqual: @"set"] &&
		    ![type isEqual: @"result"] && ![type isEqual: @"error"])
			@throw [OFInvalidArgumentException exception];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (XMPPIQ*)resultIQ
- (XMPPIQ *)resultIQ
{
	XMPPIQ *ret = [XMPPIQ IQWithType: @"result"
				      ID: [self ID]];
	[ret setTo: [self from]];
	[ret setFrom: nil];
	return ret;
}

- (XMPPIQ*)errorIQWithType: (OFString*)type
		 condition: (OFString*)condition
		      text: (OFString*)text
- (XMPPIQ *)errorIQWithType: (OFString *)type
		  condition: (OFString *)condition
		       text: (OFString *)text
{
	XMPPIQ *ret = [XMPPIQ IQWithType: @"error"
				      ID: [self ID]];
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	OFXMLElement *error = [OFXMLElement elementWithName: @"error"
						  namespace: XMPP_NS_CLIENT];

87
88
89
90
91
92
93
94
95


96
97
98
99
100
101
87
88
89
90
91
92
93


94
95
96
97
98
99
100
101







-
-
+
+






	[ret setFrom: nil];

	[pool release];

	return ret;
}

- (XMPPIQ*)errorIQWithType: (OFString*)type
		 condition: (OFString*)condition
- (XMPPIQ *)errorIQWithType: (OFString *)type
		  condition: (OFString *)condition
{
	return [self errorIQWithType: type
			   condition: condition
				text: nil];
}
@end

Modified src/XMPPJID.h from [db8b96c7e4] to [bbf76b475f].

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


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







+
+









-
+

-
+

-
+














-
+







-
+






-
+






-
+

+
+
 * 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 <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

/**
 * \brief A class for easy handling of JIDs.
 */
@interface XMPPJID: OFObject <OFCopying>
{
	OFString *_node, *_domain, *_resource;
}

/// \brief The JID's localpart
@property (copy) OFString *node;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *node;
/// \brief The JID's domainpart
@property (copy) OFString *domain;
@property (nonatomic, copy) OFString *domain;
/// \brief The JID's resourcepart
@property (copy) OFString *resource;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *resource;

/**
 * \brief Creates a new autoreleased XMPPJID.
 *
 * \return A new autoreleased XMPPJID
 */
+ (instancetype)JID;

/**
 * \brief Creates a new autoreleased XMPPJID from a string.
 *
 * \param string The string to parse into a JID object
 * \return A new autoreleased XMPPJID
 */
+ (instancetype)JIDWithString: (OFString*)string;
+ (instancetype)JIDWithString: (OFString *)string;

/**
 * \brief Initializes an already allocated XMPPJID with a string.
 *
 * \param string The string to parse into a JID object
 * \return A initialized XMPPJID
 */
- initWithString: (OFString*)string;
- initWithString: (OFString *)string;

/**
 * \brief Returns the bare JID.
 *
 * \return An OFString containing the bare JID
 */
- (OFString*)bareJID;
- (OFString *)bareJID;

/**
 * \brief Returns the full JID.
 *
 * \return An OFString containing the full JID
 */
- (OFString*)fullJID;
- (OFString *)fullJID;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPJID.m from [48ba6c2d57] to [bccd623ec3].

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
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







+
+





-
+




-
+








#include <stringprep.h>

#import "XMPPJID.h"
#import "XMPPExceptions.h"

@implementation XMPPJID
@synthesize node = _node, domain = _domain, resource = _resource;

+ (instancetype)JID
{
	return [[[self alloc] init] autorelease];
}

+ (instancetype)JIDWithString: (OFString*)string
+ (instancetype)JIDWithString: (OFString *)string
{
	return [[[self alloc] initWithString: string] autorelease];
}

- initWithString: (OFString*)string
- initWithString: (OFString *)string
{
	size_t nodesep, resourcesep;

	self = [super init];

	if (string == nil) {
		[self release];
99
100
101
102
103
104
105
106

107
108
109
110
111
112
113
101
102
103
104
105
106
107

108
109
110
111
112
113
114
115







-
+







		[new release];
		@throw e;
	}

	return new;
}

- (void)setNode: (OFString*)node
- (void)setNode: (OFString *)node
{
	OFString *old = _node;
	char *nodepart;
	Stringprep_rc rc;

	if (node == nil) {
		[old release];
128
129
130
131
132
133
134
135
136
137
138
139
140

141
142
143
144
145
146
147
130
131
132
133
134
135
136






137
138
139
140
141
142
143
144







-
-
-
-
-
-
+







	} @finally {
		free(nodepart);
	}

	[old release];
}

- (OFString*)node
{
	return [[_node copy] autorelease];
}

- (void)setDomain: (OFString*)domain
- (void)setDomain: (OFString *)domain
{
	OFString *old = _domain;
	char *srv;
	Stringprep_rc rc;

	if (((rc = stringprep_profile([domain UTF8String], &srv,
	    "Nameprep", 0)) != STRINGPREP_OK) || (srv[0] == '\0') ||
156
157
158
159
160
161
162
163
164
165
166
167
168

169
170
171
172
173
174
175
153
154
155
156
157
158
159






160
161
162
163
164
165
166
167







-
-
-
-
-
-
+







	} @finally {
		free(srv);
	}

	[old release];
}

- (OFString*)domain
{
	return [[_domain copy] autorelease];
}

- (void)setResource: (OFString*)resource
- (void)setResource: (OFString *)resource
{
	OFString *old = _resource;
	char *res;
	Stringprep_rc rc;

	if (resource == nil) {
		[old release];
190
191
192
193
194
195
196
197
198
199
200
201
202

203
204
205
206
207
208
209
210

211
212
213
214
215
216
217
218
219
220
221
222
223
224

225
226
227
228
229
230
231
182
183
184
185
186
187
188






189
190
191
192
193
194
195
196

197
198
199
200
201
202
203
204
205
206
207
208
209
210

211
212
213
214
215
216
217
218







-
-
-
-
-
-
+







-
+













-
+







	} @finally {
		free(res);
	}

	[old release];
}

- (OFString*)resource
{
	return [[_resource copy] autorelease];
}

- (OFString*)bareJID
- (OFString *)bareJID
{
	if (_node != nil)
		return [OFString stringWithFormat: @"%@@%@", _node, _domain];
	else
		return [OFString stringWithFormat: @"%@", _domain];
}

- (OFString*)fullJID
- (OFString *)fullJID
{
	/* If we don't have a resource, the full JID is equal to the bare JID */
	if (_resource == nil)
		return [self bareJID];

	if (_node != nil)
		return [OFString stringWithFormat: @"%@@%@/%@",
		       _node, _domain, _resource];
	else
		return [OFString stringWithFormat: @"%@/%@",
		       _domain, _resource];
}

- (OFString*)description
- (OFString *)description
{
	return [self fullJID];
}

- (bool)isEqual: (id)object
{
	XMPPJID *JID;

Modified src/XMPPMessage.h from [546573fa68] to [c499d3c2dc].

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


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







+
+




















-
+







-
+








-
-
+
+







-
+







-
+









-
-
+
+

+
+
 * 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 "XMPPStanza.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * \brief A class describing a message stanza.
 */
@interface XMPPMessage: XMPPStanza
/** The text content of the body of the message. */
@property (copy) OFString *body;

/**
 * \brief Creates a new autoreleased XMPPMessage.
 *
 * \return A new autoreleased XMPPMessage
 */
+ (instancetype)message;

/**
 * \brief Creates a new autoreleased XMPPMessage with the specified ID.
 *
 * \param ID The value for the stanza's id attribute
 * \return A new autoreleased XMPPMessage
 */
+ (instancetype)messageWithID: (OFString*)ID;
+ (instancetype)messageWithID: (nullable OFString *)ID;

/**
 * \brief Creates a new autoreleased XMPPMessage with the specified type.
 *
 * \param type The value for the stanza's type attribute
 * \return A new autoreleased XMPPMessage
 */
+ (instancetype)messageWithType: (OFString*)type;
+ (instancetype)messageWithType: (nullable OFString *)type;

/**
 * \brief Creates a new autoreleased XMPPMessage with the specified type and ID.
 *
 * \param type The value for the stanza's type attribute
 * \param ID The value for the stanza's id attribute
 * \return A new autoreleased XMPPMessage
 */
+ (instancetype)messageWithType: (OFString*)type
			     ID: (OFString*)ID;
+ (instancetype)messageWithType: (nullable OFString *)type
			     ID: (nullable OFString *)ID;

/**
 * \brief Initializes an already allocated XMPPMessage with the specified ID.
 *
 * \param ID The value for the stanza's id attribute
 * \return A initialized XMPPMessage
 */
- initWithID: (OFString*)ID;
- initWithID: (nullable OFString *)ID;

/**
 * \brief Initializes an already allocated XMPPMessage with the specified type.
 *
 * \param type The value for the stanza's type attribute
 * \return A initialized XMPPMessage
 */
- initWithType: (OFString*)type;
- initWithType: (nullable OFString *)type;

/**
 * \brief Initializes an already allocated XMPPMessage with the specified type
 *	  and ID.
 *
 * \param type The value for the stanza's type attribute
 * \param ID The value for the stanza's id attribute
 * \return A initialized XMPPMessage
 */
- initWithType: (OFString*)type
	    ID: (OFString*)ID;
- initWithType: (nullable OFString *)type
	    ID: (nullable OFString *)ID OF_DESIGNATED_INITIALIZER;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPMessage.m from [19d004e57c] to [64ae4d9271].

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
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







-
+




-
+




-
-
+
+











-
+





-
+





-
-
+
+






-
+













-
+






@implementation XMPPMessage
+ (instancetype)message
{
	return [[[self alloc] init] autorelease];
}

+ (instancetype)messageWithID: (OFString*)ID
+ (instancetype)messageWithID: (OFString *)ID
{
	return [[[self alloc] initWithID: ID] autorelease];
}

+ (instancetype)messageWithType: (OFString*)type
+ (instancetype)messageWithType: (OFString *)type
{
	return [[[self alloc] initWithType: type] autorelease];
}

+ (instancetype)messageWithType: (OFString*)type
	       ID: (OFString*)ID
+ (instancetype)messageWithType: (OFString *)type
			     ID: (OFString *)ID
{
	return [[[self alloc] initWithType: type
					ID: ID] autorelease];
}

- init
{
	return [self initWithType: nil
			       ID: nil];
}

- initWithID: (OFString*)ID
- initWithID: (OFString *)ID
{
	return [self initWithType: nil
			       ID: ID];
}

- initWithType: (OFString*)type
- initWithType: (OFString *)type
{
	return [self initWithType: type
			       ID: nil];
}

- initWithType: (OFString*)type
	    ID: (OFString*)ID
- initWithType: (OFString *)type
	    ID: (OFString *)ID
{
	return [super initWithName: @"message"
			      type: type
				ID: ID];
}

- (void)setBody: (OFString*)body
- (void)setBody: (OFString *)body
{
	OFXMLElement *oldBody = [self elementForName: @"body"
					   namespace: XMPP_NS_CLIENT];

	if (oldBody != nil)
		[self removeChild: oldBody];

	if (body != nil)
		[self addChild: [OFXMLElement elementWithName: @"body"
						    namespace: XMPP_NS_CLIENT
						  stringValue: body]];
}

- (OFString*)body
- (OFString *)body
{
	return [[self elementForName: @"body"
			   namespace: XMPP_NS_CLIENT] stringValue];
}
@end

Modified src/XMPPMulticastDelegate.h from [9c7e2a3fbd] to [51673325c7].

18
19
20
21
22
23
24


25
26
27
28
29
30
31
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33







+
+







 * 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 <ObjFW/OFObject.h>

OF_ASSUME_NONNULL_BEGIN

@class OFDataArray;

/**
 * \brief A class to provide multiple delegates in a single class
 */
@interface XMPPMulticastDelegate: OFObject
{
49
50
51
52
53
54
55
56

57
58
59
60
61
62
63
64
65
66
67


68


51
52
53
54
55
56
57

58
59
60
61
62
63
64
65
66
67


68
69
70
71
72







-
+









-
-
+
+

+
+
/**
 * \brief Broadcasts a selector with an object to all registered delegates.
 *
 * \param selector The selector to broadcast
 * \param object The object to broadcast
 */
- (bool)broadcastSelector: (SEL)selector
	       withObject: (id)object;
	       withObject: (nullable id)object;

/**
 * \brief Broadcasts a selector with two objects to all registered delegates.
 *
 * \param selector The selector to broadcast
 * \param object1 The first object to broadcast
 * \param object2 The second object to broadcast
 */
- (bool)broadcastSelector: (SEL)selector
	       withObject: (id)object1
	       withObject: (id)object2;
	       withObject: (nullable id)object1
	       withObject: (nullable id)object2;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPPLAINAuth.h from [5dfa895145] to [3c1bd83caf].

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


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







+
+











-
-
+
+










-
-
-
+
+
+

+
+
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#import <ObjFW/ObjFW.h>
#import "XMPPAuthenticator.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * \brief A class to authenticate using SASL PLAIN
 */
@interface XMPPPLAINAuth: XMPPAuthenticator
/**
 * \brief Creates a new autoreleased XMPPPLAINAuth with an authcid and password.
 *
 * \param authcid The authcid to authenticate with
 * \param password The password to authenticate with
 * \return A new autoreleased XMPPPLAINAuth
 */
+ (instancetype)PLAINAuthWithAuthcid: (OFString*)authcid
			    password: (OFString*)password;
+ (instancetype)PLAINAuthWithAuthcid: (nullable OFString *)authcid
			    password: (nullable OFString *)password;

/**
 * \brief Creates a new autoreleased XMPPPLAINAuth with an authzid, authcid and
 *	  password.
 *
 * \param authzid The authzid to get authorization for
 * \param authcid The authcid to authenticate with
 * \param password The password to authenticate with
 * \return A new autoreleased XMPPPLAINAuth
 */
+ (instancetype)PLAINAuthWithAuthzid: (OFString*)authzid
			     authcid: (OFString*)authcid
	      password: (OFString*)password;
+ (instancetype)PLAINAuthWithAuthzid: (nullable OFString *)authzid
			     authcid: (nullable OFString *)authcid
			    password: (nullable OFString *)password;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPPLAINAuth.m from [9d4992173d] to [9f0783c62c].

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
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







-
-
+
+





-
-
-
+
+
+






-
+







# include "config.h"
#endif

#import "XMPPPLAINAuth.h"
#import "XMPPExceptions.h"

@implementation XMPPPLAINAuth
+ (instancetype)PLAINAuthWithAuthcid: (OFString*)authcid
			    password: (OFString*)password
+ (instancetype)PLAINAuthWithAuthcid: (OFString *)authcid
			    password: (OFString *)password
{
	return [[[self alloc] initWithAuthcid: authcid
				     password: password] autorelease];
}

+ (instancetype)PLAINAuthWithAuthzid: (OFString*)authzid
			     authcid: (OFString*)authcid
			    password: (OFString*)password
+ (instancetype)PLAINAuthWithAuthzid: (OFString *)authzid
			     authcid: (OFString *)authcid
			    password: (OFString *)password
{
	return [[[self alloc] initWithAuthzid: authzid
				      authcid: authcid
				     password: password] autorelease];
}

- (OFDataArray*)initialMessage
- (OFDataArray *)initialMessage
{
	OFDataArray *message = [OFDataArray dataArray];

	/* authzid */
	if (_authzid)
		[message addItem: _authzid];

Modified src/XMPPPresence.h from [3d03c9db92] to [0497c404f5].

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
102
103
104
105
106
107
108
109


110


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
102
103
104

105
106
107
108
109
110
111
112
113
114


115
116
117
118
119







+
+









+
+
+
+
+



-
+




-
+




-
+














-
+







-
+









-
-
+
+







-
+







-
+









-
-
+
+

+
+
 * 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 "XMPPStanza.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * \brief A class describing a presence stanza.
 */
@interface XMPPPresence: XMPPStanza <OFComparing>
{
	OFString *_status, *_show;
	OFNumber *_priority;
}

/**
 * The value of the stanza's type attribute.
 */
@property OF_NULL_RESETTABLE_PROPERTY (nonatomic, copy) OFString *type;

/**
 * The text content of the status element.
 */
@property (copy) OFString *status;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *status;

/**
 * The text content of the show element of the presence stanza.
 */
@property (copy) OFString *show;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *show;

/**
 * The numeric content of the priority element.
 */
@property (copy) OFNumber *priority;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFNumber *priority;

/**
 * \brief Creates a new autoreleased XMPPPresence.
 *
 * \return A new autoreleased XMPPPresence
 */
+ (instancetype)presence;

/**
 * \brief Creates a new autoreleased XMPPPresence with the specified ID.
 *
 * \param ID The value for the stanza's id attribute
 * \return A new autoreleased XMPPPresence
 */
+ (instancetype)presenceWithID: (OFString*)ID;
+ (instancetype)presenceWithID: (nullable OFString *)ID;

/**
 * \brief Creates a new autoreleased XMPPPresence with the specified type.
 *
 * \param type The value for the stanza's type attribute
 * \return A new autoreleased XMPPPresence
 */
+ (instancetype)presenceWithType: (OFString*)type;
+ (instancetype)presenceWithType: (nullable OFString *)type;

/**
 * \brief Creates a new autoreleased XMPPPresence with the specified type and
 *	  ID.
 *
 * \param type The value for the stanza's type attribute
 * \param ID The value for the stanza's id attribute
 * \return A new autoreleased XMPPPresence
 */
+ (instancetype)presenceWithType: (OFString*)type
			      ID: (OFString*)ID;
+ (instancetype)presenceWithType: (nullable OFString *)type
			      ID: (nullable OFString *)ID;

/**
 * \brief Initializes an already allocated XMPPPresence with the specified ID.
 *
 * \param ID The value for the stanza's id attribute
 * \return A initialized XMPPPresence
 */
- initWithID: (OFString*)ID;
- initWithID: (nullable OFString *)ID;

/**
 * \brief Initializes an already allocated XMPPPresence with the specified type.
 *
 * \param type The value for the stanza's type attribute
 * \return A initialized XMPPPresence
 */
- initWithType: (OFString*)type;
- initWithType: (nullable OFString *)type;

/**
 * \brief Initializes an already allocated XMPPPresence with the specified type
 *	  and ID.
 *
 * \param type The value for the stanza's type attribute
 * \param ID The value for the stanza's id attribute
 * \return A initialized XMPPPresence
 */
- initWithType: (OFString*)type
	    ID: (OFString*)ID;
- initWithType: (nullable OFString *)type
	    ID: (nullable OFString *)ID;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPPresence.m from [5c903aded4] to [defd4ee1a6].

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
102
103
104
105
106
107
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

102
103
104
105
106
107
108
109







+
+





-
+




-
+




-
-
+
+











-
+





-
+





-
-
+
+






-
+







	if ([show isEqual: @"xa"])
		return 4;

	OF_ENSURE(0);
}

@implementation XMPPPresence
@dynamic type;

+ (instancetype)presence
{
	return [[[self alloc] init] autorelease];
}

+ (instancetype)presenceWithID: (OFString*)ID
+ (instancetype)presenceWithID: (OFString *)ID
{
	return [[[self alloc] initWithID: ID] autorelease];
}

+ (instancetype)presenceWithType: (OFString*)type
+ (instancetype)presenceWithType: (OFString *)type
{
	return [[[self alloc] initWithType: type] autorelease];
}

+ (instancetype)presenceWithType: (OFString*)type
			      ID: (OFString*)ID
+ (instancetype)presenceWithType: (OFString *)type
			      ID: (OFString *)ID
{
	return [[[self alloc] initWithType: type
					ID: ID] autorelease];
}

- init
{
	return [self initWithType: nil
			       ID: nil];
}

- initWithID: (OFString*)ID
- initWithID: (OFString *)ID
{
	return [self initWithType: nil
			       ID: ID];
}

- initWithType: (OFString*)type
- initWithType: (OFString *)type
{
	return [self initWithType: type
			       ID: nil];
}

- initWithType: (OFString*)type
	    ID: (OFString*)ID
- initWithType: (OFString *)type
	    ID: (OFString *)ID
{
	return [super initWithName: @"presence"
			      type: type
				ID: ID];
}

- initWithElement: (OFXMLElement*)element
- initWithElement: (OFXMLElement *)element
{
	self = [super initWithElement: element];

	@try {
		OFXMLElement *subElement;

		if ((subElement = [element elementForName: @"show"
131
132
133
134
135
136
137
138

139
140
141


142
143

144
145
146

147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170

171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194

195
196
197
198
199
200
201
202
203
204
205
206
207
208

209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

238
239
240
241
242
243
244
133
134
135
136
137
138
139

140
141


142
143
144

145
146
147

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166






167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185






186
187
188
189
190
191
192
193
194
195
196
197
198
199

200
201
202
203
204
205
206
207
208
209
210





211
212
213
214
215
216
217
218
219
220
221
222
223

224
225
226
227
228
229
230
231







-
+

-
-
+
+

-
+


-
+


















-
-
-
-
-
-
+


















-
-
-
-
-
-
+













-
+










-
-
-
-
-













-
+







	[_status release];
	[_show release];
	[_priority release];

	[super dealloc];
}

- (OFString*)type
- (void)setType: (OFString *)type
{
	if (_type == nil)
		return @"available";
	if (type == nil)
		type = @"available";

	return [[_type copy] autorelease];
	[super setType: type];
}

- (void)setShow: (OFString*)show
- (void)setShow: (OFString *)show
{
	OFXMLElement *oldShow = [self elementForName: @"show"
					   namespace: XMPP_NS_CLIENT];
	OFString *old;

	if (oldShow != nil)
		[self removeChild: oldShow];

	if (show != nil)
		[self addChild: [OFXMLElement elementWithName: @"show"
						    namespace: XMPP_NS_CLIENT
						  stringValue: show]];

	old = _show;
	_show = [show copy];
	[old release];
}

- (OFString*)show
{
	return [[_show copy] autorelease];
}

- (void)setStatus: (OFString*)status
- (void)setStatus: (OFString *)status
{
	OFXMLElement *oldStatus = [self elementForName: @"status"
					     namespace: XMPP_NS_CLIENT];
	OFString *old;

	if (oldStatus != nil)
		[self removeChild: oldStatus];

	if (status != nil)
		[self addChild: [OFXMLElement elementWithName: @"status"
						    namespace: XMPP_NS_CLIENT
						  stringValue: status]];

	old = _status;
	_status = [status copy];
	[old release];
}

- (OFString*)status
{
	return [[_status copy] autorelease];
}

- (void)setPriority: (OFNumber*)priority
- (void)setPriority: (OFNumber *)priority
{
	intmax_t prio = [priority intMaxValue];
	OFNumber *old;

	if ((prio < -128) || (prio > 127))
		@throw [OFInvalidArgumentException exception];

	OFXMLElement *oldPriority = [self elementForName: @"priority"
					       namespace: XMPP_NS_CLIENT];

	if (oldPriority != nil)
		[self removeChild: oldPriority];

	OFString* priority_s =
	OFString *priority_s =
	    [OFString stringWithFormat: @"%" @PRId8, [priority int8Value]];
	[self addChild: [OFXMLElement elementWithName: @"priority"
					    namespace: XMPP_NS_CLIENT
					  stringValue: priority_s]];

	old = _priority;
	_priority = [priority copy];
	[old release];
}

- (OFNumber*)priority
{
	return [[_priority copy] autorelease];
}

- (of_comparison_result_t)compare: (id <OFComparing>)object
{
	XMPPPresence *otherPresence;
	OFNumber *otherPriority;
	OFString *otherShow;
	of_comparison_result_t priorityOrder;

	if (object == self)
		return OF_ORDERED_SAME;

	if (![object isKindOfClass: [XMPPPresence class]])
		@throw [OFInvalidArgumentException exception];

	otherPresence = (XMPPPresence*)object;
	otherPresence = (XMPPPresence *)object;
	otherPriority = [otherPresence priority];
	if (otherPriority == nil)
		otherPriority = [OFNumber numberWithInt8: 0];

	if (_priority != nil)
		priorityOrder = [_priority compare: otherPriority];
	else

Modified src/XMPPRoster.h from [9f74b009a4] to [7a62e71bc2].

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
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







+
+










-
-
-







-
+







-
-
+
+







 */

#import <ObjFW/ObjFW.h>

#import "XMPPConnection.h"
#import "XMPPStorage.h"

OF_ASSUME_NONNULL_BEGIN

@class XMPPRosterItem;
@class XMPPIQ;
@class XMPPRoster;
@class XMPPMulticastDelegate;

/**
 * \brief A protocol that should be (partially) implemented by delegates
 *	  of a XMPPRoster
 */
@protocol XMPPRosterDelegate
#ifndef XMPP_ROSTER_M
    <OFObject>
#endif
@optional
/**
 * \brief This callback is called after the roster was received (as a result of
 *	  calling -requestRoster).
 *
 * \param roster The roster that was received
 */
- (void)rosterWasReceived: (XMPPRoster*)roster;
- (void)rosterWasReceived: (XMPPRoster *)roster;

/**
 * \brief This callback is called whenever a roster push was received.
 *
 * \param roster The roster that was updated by the roster push
 * \param rosterItem The roster item received in the push
 */
-         (void)roster: (XMPPRoster*)roster
  didReceiveRosterItem: (XMPPRosterItem*)rosterItem;
-         (void)roster: (XMPPRoster *)roster
  didReceiveRosterItem: (XMPPRosterItem *)rosterItem;
@end

/**
 * \brief A class implementing roster related functionality.
 */
@interface XMPPRoster: OFObject <XMPPConnectionDelegate>
{
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
102
103
104
105
106
107
108
109
110

111
112
113
114
115
116
117

118
119
120
121
122
123
124

125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

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
102
103
104
105
106
107
108
109
110
111

112
113
114
115
116
117
118

119
120
121
122
123
124
125

126
127
128
129
130
131
132
133
134
135
136
137
138
139
140





141
142


143







-
+





-
+
+
+
+




-
-
+
+


-
+











-
+






-
+






-
+














-
-
-
-
-


-
-
+
@property (readonly, assign) XMPPConnection *connection;

/**
 * \brief An object for data storage, conforming to the XMPPStorage protocol.
 *
 * Inherited from the connection if not overridden.
 */
@property (assign) id <XMPPStorage> dataStorage;
@property (nonatomic, assign) id <XMPPStorage> dataStorage;

/**
 * \brief The list of contacts as an OFDictionary with the bare JID as a string
 *	  as key.
 */
@property (readonly, copy) OFDictionary *rosterItems;
@property (readonly, nonatomic)
    OFDictionary OF_GENERIC(OFString *, XMPPRosterItem *) *rosterItems;

- init OF_UNAVAILABLE;

/**
 * \brief Initializes an already allocated XMPPRoster.
 *
 * \param connection The connection roster related stanzas
 *	  are send and received over
 * \param connection The connection roster related stanzas are send and
 *		     received over
 * \return An initialized XMPPRoster
 */
- initWithConnection: (XMPPConnection*)connection;
- initWithConnection: (XMPPConnection *)connection OF_DESIGNATED_INITIALIZER;

/**
 * \brief Requests the roster from the server.
 */
- (void)requestRoster;

/**
 * \brief Adds a new contact to the roster.
 *
 * \param rosterItem The roster item to add to the roster
 */
- (void)addRosterItem: (XMPPRosterItem*)rosterItem;
- (void)addRosterItem: (XMPPRosterItem *)rosterItem;

/**
 * \brief Updates an already existing contact in the roster.
 *
 * \param rosterItem The roster item to update
 */
- (void)updateRosterItem: (XMPPRosterItem*)rosterItem;
- (void)updateRosterItem: (XMPPRosterItem *)rosterItem;

/**
 * \brief Delete a contact from the roster.
 *
 * \param rosterItem The roster item to delete
 */
- (void)deleteRosterItem: (XMPPRosterItem*)rosterItem;
- (void)deleteRosterItem: (XMPPRosterItem *)rosterItem;

/**
 * \brief Adds the specified delegate.
 *
 * \param delegate The delegate to add
 */
- (void)addDelegate: (id <XMPPRosterDelegate>)delegate;

/**
 * \brief Removes the specified delegate.
 *
 * \param delegate The delegate to remove
 */
- (void)removeDelegate: (id <XMPPRosterDelegate>)delegate;

- (void)XMPP_updateRosterItem: (XMPPRosterItem*)rosterItem;
- (void)XMPP_handleInitialRosterForConnection: (XMPPConnection*)connection
					   IQ: (XMPPIQ*)iq;
- (XMPPRosterItem*)XMPP_rosterItemWithXMLElement: (OFXMLElement*)element;
@end

@interface OFObject (XMPPRosterDelegate) <XMPPRosterDelegate>
@end
OF_ASSUME_NONNULL_END

Modified src/XMPPRoster.m from [8b7b6f027e] to [7642c78519].

34
35
36
37
38
39
40











41
42
43
44
45





46

47
48
49
50
51
52
53
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







+
+
+
+
+
+
+
+
+
+
+





+
+
+
+
+
-
+







#import "XMPPRoster.h"
#import "XMPPRosterItem.h"
#import "XMPPConnection.h"
#import "XMPPIQ.h"
#import "XMPPJID.h"
#import "XMPPMulticastDelegate.h"
#import "namespaces.h"

OF_ASSUME_NONNULL_BEGIN

@interface XMPPRoster ()
- (void)XMPP_updateRosterItem: (XMPPRosterItem *)rosterItem;
- (void)XMPP_handleInitialRosterForConnection: (XMPPConnection *)connection
					   IQ: (XMPPIQ *)IQ;
- (XMPPRosterItem *)XMPP_rosterItemWithXMLElement: (OFXMLElement *)element;
@end

OF_ASSUME_NONNULL_END

@implementation XMPPRoster
@synthesize connection = _connection, dataStorage = _dataStorage;
@synthesize rosterItems = _rosterItems;

- init
{
	OF_INVALID_INIT_METHOD
}

- initWithConnection: (XMPPConnection*)connection
- initWithConnection: (XMPPConnection *)connection
{
	self = [super init];

	@try {
		_rosterItems = [[OFMutableDictionary alloc] init];
		_connection = connection;
		[_connection addDelegate: self];
98
99
100
101
102
103
104
105
106


107
108
109
110
111
112
113

114
115
116
117
118
119

120
121
122
123

124
125
126
127
128
129
130
114
115
116
117
118
119
120


121
122
123
124
125
126
127
128

129
130
131
132
133
134

135
136
137
138

139
140
141
142
143
144
145
146







-
-
+
+






-
+





-
+



-
+








	[_connection sendIQ: iq
	     callbackTarget: self
		   selector: @selector(XMPP_handleInitialRosterForConnection:
				IQ:)];
}

- (bool)connection: (XMPPConnection*)connection
      didReceiveIQ: (XMPPIQ*)iq
- (bool)connection: (XMPPConnection *)connection
      didReceiveIQ: (XMPPIQ *)IQ
{
	OFXMLElement *rosterElement;
	OFXMLElement *element;
	XMPPRosterItem *rosterItem;
	OFString *origin;

	rosterElement = [iq elementForName: @"query"
	rosterElement = [IQ elementForName: @"query"
				 namespace: XMPP_NS_ROSTER];

	if (rosterElement == nil)
		return false;

	if (![[iq type] isEqual: @"set"])
	if (![[IQ type] isEqual: @"set"])
		return false;

	// Ensure the roster push has been sent by the server
	origin = [[iq from] fullJID];
	origin = [[IQ from] fullJID];
	if (origin != nil && ![origin isEqual: [[connection JID] bareJID]])
		return false;

	element = [rosterElement elementForName: @"item"
				      namespace: XMPP_NS_ROSTER];

	if (element != nil) {
142
143
144
145
146
147
148
149

150
151
152
153
154

155
156
157
158
159

160
161
162
163
164
165
166
158
159
160
161
162
163
164

165
166
167
168
169

170
171
172
173
174

175
176
177
178
179
180
181
182







-
+




-
+




-
+







		OFString *ver =
		    [[rosterElement attributeForName: @"ver"] stringValue];
		[_dataStorage setStringValue: ver
				     forPath: @"roster.ver"];
		[_dataStorage save];
	}

	[connection sendStanza: [iq resultIQ]];
	[connection sendStanza: [IQ resultIQ]];

	return true;
}

- (void)addRosterItem: (XMPPRosterItem*)rosterItem
- (void)addRosterItem: (XMPPRosterItem *)rosterItem
{
	[self updateRosterItem: rosterItem];
}

- (void)updateRosterItem: (XMPPRosterItem*)rosterItem
- (void)updateRosterItem: (XMPPRosterItem *)rosterItem
{
	XMPPIQ *IQ = [XMPPIQ IQWithType: @"set"
				     ID: [_connection generateStanzaID]];
	OFXMLElement *query = [OFXMLElement elementWithName: @"query"
						  namespace: XMPP_NS_ROSTER];
	OFXMLElement *item = [OFXMLElement elementWithName: @"item"
						 namespace: XMPP_NS_ROSTER];
181
182
183
184
185
186
187
188

189
190
191
192
193
194
195
197
198
199
200
201
202
203

204
205
206
207
208
209
210
211







-
+








	[query addChild: item];
	[IQ addChild: query];

	[_connection sendStanza: IQ];
}

- (void)deleteRosterItem: (XMPPRosterItem*)rosterItem
- (void)deleteRosterItem: (XMPPRosterItem *)rosterItem
{
	XMPPIQ *IQ = [XMPPIQ IQWithType: @"set"
				     ID: [_connection generateStanzaID]];
	OFXMLElement *query = [OFXMLElement elementWithName: @"query"
						  namespace: XMPP_NS_ROSTER];
	OFXMLElement *item = [OFXMLElement elementWithName: @"item"
						 namespace: XMPP_NS_ROSTER];
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

238
239
240
241
242
243
244
236
237
238
239
240
241
242











243
244
245
246
247
248
249
250







-
-
-
-
-
-
-
-
-
-
-
+







	if (_rosterRequested)
		/* FIXME: Find a better exception! */
		@throw [OFInvalidArgumentException exception];

	_dataStorage = dataStorage;
}

- (XMPPConnection*)connection
{
	return _connection;
}

- (id <XMPPStorage>)dataStorage
{
	return _dataStorage;
}

- (void)XMPP_updateRosterItem: (XMPPRosterItem*)rosterItem
- (void)XMPP_updateRosterItem: (XMPPRosterItem *)rosterItem
{
	if ([_connection supportsRosterVersioning]) {
		OFMutableDictionary *items = [[[_dataStorage dictionaryForPath:
		    @"roster.items"] mutableCopy] autorelease];

		if (items == nil)
			items = [OFMutableDictionary dictionary];
270
271
272
273
274
275
276
277

278
279
280
281
282
283
284
276
277
278
279
280
281
282

283
284
285
286
287
288
289
290







-
+







	if (![[rosterItem subscription] isEqual: @"remove"])
		[_rosterItems setObject: rosterItem
				 forKey: [[rosterItem JID] bareJID]];
	else
		[_rosterItems removeObjectForKey: [[rosterItem JID] bareJID]];
}

- (XMPPRosterItem*)XMPP_rosterItemWithXMLElement: (OFXMLElement*)element
- (XMPPRosterItem *)XMPP_rosterItemWithXMLElement: (OFXMLElement *)element
{
	OFString *subscription;
	OFEnumerator *groupEnumerator;
	OFXMLElement *groupElement;
	OFMutableArray *groups = [OFMutableArray array];
	XMPPRosterItem *rosterItem = [XMPPRosterItem rosterItem];
	[rosterItem setJID: [XMPPJID JIDWithString:
306
307
308
309
310
311
312
313
314


315
316

317
318
319
320
321

322
323
324
325
326
327
328
312
313
314
315
316
317
318


319
320
321

322





323
324
325
326
327
328
329
330







-
-
+
+

-
+
-
-
-
-
-
+








	if ([groups count] > 0)
		[rosterItem setGroups: groups];

	return rosterItem;
}

- (void)XMPP_handleInitialRosterForConnection: (XMPPConnection*)connection
					   IQ: (XMPPIQ*)IQ
- (void)XMPP_handleInitialRosterForConnection: (XMPPConnection *)connection
					   IQ: (XMPPIQ *)IQ
{
	OFXMLElement *rosterElement;
	OFXMLElement *rosterElement = [IQ elementForName: @"query"
	OFEnumerator *enumerator;
	OFXMLElement *element;

	rosterElement = [IQ elementForName: @"query"
				 namespace: XMPP_NS_ROSTER];
					       namespace: XMPP_NS_ROSTER];

	if ([connection supportsRosterVersioning]) {
		if (rosterElement == nil) {
			OFDictionary *items = [_dataStorage
			    dictionaryForPath: @"roster.items"];
			OFEnumerator *enumerator = [items objectEnumerator];
			OFDictionary *item;
346
347
348
349
350
351
352
353

354
355
356
357
358
359
360
361
348
349
350
351
352
353
354

355

356
357
358
359
360
361
362







-
+
-







						 forKey: [JID bareJID]];
			}
		} else
			[_dataStorage setDictionary: nil
					    forPath: @"roster.items"];
	}

	enumerator = [[rosterElement children] objectEnumerator];
	for (OFXMLElement *element in [rosterElement children]) {
	while ((element = [enumerator nextObject]) != nil) {
		OFAutoreleasePool *pool;
		XMPPRosterItem *rosterItem;

		if (![[element name] isEqual: @"item"] ||
		    ![[element namespace] isEqual: XMPP_NS_ROSTER])
			continue;

Modified src/XMPPRosterItem.h from [fbf33416fb] to [b424f574a0].

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


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







+
+














-
+

-
+

-
+

-
+








+
+
 * 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 <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@class XMPPJID;

/**
 * \brief A class for representing an item in the roster.
 */
@interface XMPPRosterItem: OFObject
{
	XMPPJID *_JID;
	OFString *_name;
	OFString *_subscription;
	OFArray *_groups;
}

/// \brief The JID of the roster item
@property (copy) XMPPJID *JID;
@property (nonatomic, copy) XMPPJID *JID;
/// \brief The name of the roster item to show to the user
@property (copy) OFString *name;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *name;
/// \brief The subscription for the roster item
@property (copy) OFString *subscription;
@property (nonatomic, copy) OFString *subscription;
/// \brief An array of groups in which the roster item is
@property (copy) OFArray *groups;
@property (nonatomic, copy) OFArray OF_GENERIC(OFString *) *groups;

/**
 * \brief Creates a new autoreleased roster item.
 *
 * \return A new autoreleased roster item.
 */
+ (instancetype)rosterItem;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPRosterItem.m from [896b51646c] to [5e285f3028].

62
63
64
65
66
67
68
69

70
71
72
73
74
75
62
63
64
65
66
67
68

69
70
71
72
73
74
75







-
+






		[new release];
		@throw e;
	}

	return new;
}

- (OFString*)description
- (OFString *)description
{
	return [OFString stringWithFormat: @"<XMPPRosterItem, JID=%@, name=%@, "
					   @"subscription=%@, groups=%@>",
					   _JID, _name, _subscription, _groups];
}
@end

Modified src/XMPPSCRAMAuth.h from [caa9d18a81] to [fc2ab6d5ef].

20
21
22
23
24
25
26


27
28
29
30
31
32
33
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35







+
+







 * POSSIBILITY OF SUCH DAMAGE.
 */

#import <ObjFW/ObjFW.h>
#import "XMPPAuthenticator.h"
#import "XMPPConnection.h"

OF_ASSUME_NONNULL_BEGIN

/**
 * \brief A class to authenticate using SCRAM
 */
@interface XMPPSCRAMAuth: XMPPAuthenticator
{
	Class _hashType;
	OFString *_cNonce;
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
102
103
104
105
106
107
108
109




110
111


112
113
114

115
116
117
118
119
120
121
122
123
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
102
103
104
105
106
107
108
109
110
111
112
113




114
115
116
117
118

119
120
121


122
















-
-
-
+
+
+















-
-
-
-
+
+
+
+



+
+
+
+
+
+











-
-
-
+
+
+















-
-
-
-
+
+
+
+

-
+
+

-
-
+
-
-
-
-
-
-
-
-
-
 * \param authcid The authcid to authenticate with
 * \param password The password to authenticate with
 * \param connection The connection over which authentication is done
 * \param hash The class to use for calulating hashes
 * \param plusAvailable Whether the PLUS variant was offered
 * \return A new autoreleased XMPPSCRAMAuth
 */
+ (instancetype)SCRAMAuthWithAuthcid: (OFString*)authcid
			    password: (OFString*)password
			  connection: (XMPPConnection*)connection
+ (instancetype)SCRAMAuthWithAuthcid: (nullable OFString *)authcid
			    password: (nullable OFString *)password
			  connection: (XMPPConnection *)connection
				hash: (Class)hash
		       plusAvailable: (bool)plusAvailable;

/**
 * \brief Creates a new autoreleased XMPPSCRAMAuth with an authzid, authcid and
 *	  password.
 *
 * \param authzid The authzid to get authorization for
 * \param authcid The authcid to authenticate with
 * \param password The password to authenticate with
 * \param connection The connection over which authentication is done
 * \param hash The class to use for calulating hashes
 * \param plusAvailable Whether the PLUS variant was offered
 * \return A new autoreleased XMPPSCRAMAuth
 */
+ (instancetype)SCRAMAuthWithAuthzid: (OFString*)authzid
			     authcid: (OFString*)authcid
			    password: (OFString*)password
			  connection: (XMPPConnection*)connection
+ (instancetype)SCRAMAuthWithAuthzid: (nullable OFString *)authzid
			     authcid: (nullable OFString *)authcid
			    password: (nullable OFString *)password
			  connection: (XMPPConnection *)connection
				hash: (Class)hash
		       plusAvailable: (bool)plusAvailable;

- initWithAuthcid: (nullable OFString *)authcid
	 password: (nullable OFString *)password OF_UNAVAILABLE;
- initWithAuthzid: (nullable OFString *)authzid
	  authcid: (nullable OFString *)authcid
	 password: (nullable OFString *)password OF_UNAVAILABLE;

/**
 * \brief Initializes an already allocated XMPPSCRAMAuth with an authcid and
 *	  password.
 *
 * \param authcid The authcid to authenticate with
 * \param password The password to authenticate with
 * \param connection The connection over which authentication is done
 * \param hash The class to use for calulating hashes
 * \param plusAvailable Whether the PLUS variant was offered
 * \return A initialized XMPPSCRAMAuth
 */
- initWithAuthcid: (OFString*)authcid
	 password: (OFString*)password
       connection: (XMPPConnection*)connection
- initWithAuthcid: (nullable OFString *)authcid
	 password: (nullable OFString *)password
       connection: (XMPPConnection *)connection
	     hash: (Class)hash
    plusAvailable: (bool)plusAvailable;

/**
 * \brief Initializes an already allocated XMPPSCRAMAuth with a authzid,
 *	  authcid and password.
 *
 * \param authzid The authzid to get authorization for
 * \param authcid The authcid to authenticate with
 * \param password The password to authenticate with
 * \param connection The connection over which authentication is done
 * \param hash The class to use for calulating hashes
 * \param plusAvailable Whether the PLUS variant was offered
 * \return A initialized XMPPSCRAMAuth
 */
- initWithAuthzid: (OFString*)authzid
	  authcid: (OFString*)authcid
	 password: (OFString*)password
       connection: (XMPPConnection*)connection
- initWithAuthzid: (nullable OFString *)authzid
	  authcid: (nullable OFString *)authcid
	 password: (nullable OFString *)password
       connection: (XMPPConnection *)connection
	     hash: (Class)hash
    plusAvailable: (bool)plusAvailable;
    plusAvailable: (bool)plusAvailable OF_DESIGNATED_INITIALIZER;
@end

/// \cond internal
- (OFString*)XMPP_genNonce;
OF_ASSUME_NONNULL_END
- (const uint8_t*)XMPP_HMACWithKey: (OFDataArray*)key
			data: (OFDataArray*)data;
- (OFDataArray*)XMPP_hiWithData: (OFDataArray*)str
			   salt: (OFDataArray*)salt
		 iterationCount: (intmax_t)i;
- (OFDataArray*)XMPP_parseServerFirstMessage: (OFDataArray*)data;
- (OFDataArray*)XMPP_parseServerFinalMessage: (OFDataArray*)data;
/// \endcond
@end

Modified src/XMPPSCRAMAuth.m from [ec991072f9] to [e2f43c5d67].

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
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
102
103
104
105
106
107
108








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
+
+
+










-
-
-
-
+
+
+
+











-
-
-
+
+
+











-
-
-
-
+
+
+
+







#import <ObjOpenSSL/SSLSocket.h>

#import "XMPPSCRAMAuth.h"
#import "XMPPExceptions.h"

#define HMAC_IPAD 0x36
#define HMAC_OPAD 0x5c

OF_ASSUME_NONNULL_BEGIN

@interface XMPPSCRAMAuth ()
- (OFString *)XMPP_genNonce;
- (const uint8_t *)XMPP_HMACWithKey: (OFDataArray *)key
			data: (OFDataArray *)data;
- (OFDataArray *)XMPP_hiWithData: (OFDataArray *)str
			   salt: (OFDataArray *)salt
		 iterationCount: (intmax_t)i;
- (OFDataArray *)XMPP_parseServerFirstMessage: (OFDataArray *)data;
- (OFDataArray *)XMPP_parseServerFinalMessage: (OFDataArray *)data;
@end

OF_ASSUME_NONNULL_END

@implementation XMPPSCRAMAuth
+ (instancetype)SCRAMAuthWithAuthcid: (OFString*)authcid
			    password: (OFString*)password
			  connection: (XMPPConnection*)connection
+ (instancetype)SCRAMAuthWithAuthcid: (OFString *)authcid
			    password: (OFString *)password
			  connection: (XMPPConnection *)connection
				hash: (Class)hash
		       plusAvailable: (bool)plusAvailable
{
	return [[[self alloc] initWithAuthcid: authcid
				     password: password
				   connection: connection
					 hash: hash
				plusAvailable: plusAvailable] autorelease];
}

+ (instancetype)SCRAMAuthWithAuthzid: (OFString*)authzid
			     authcid: (OFString*)authcid
			    password: (OFString*)password
			  connection: (XMPPConnection*)connection
+ (instancetype)SCRAMAuthWithAuthzid: (OFString *)authzid
			     authcid: (OFString *)authcid
			    password: (OFString *)password
			  connection: (XMPPConnection *)connection
				hash: (Class)hash
		       plusAvailable: (bool)plusAvailable
{
	return [[[self alloc] initWithAuthzid: authzid
				      authcid: authcid
				     password: password
				   connection: connection
					 hash: hash
				plusAvailable: plusAvailable] autorelease];
}

- initWithAuthcid: (OFString*)authcid
	 password: (OFString*)password
       connection: (XMPPConnection*)connection
- initWithAuthcid: (OFString *)authcid
	 password: (OFString *)password
       connection: (XMPPConnection *)connection
	     hash: (Class)hash
    plusAvailable: (bool)plusAvailable
{
	return [self initWithAuthzid: nil
			     authcid: authcid
			    password: password
			  connection: connection
				hash: hash
		       plusAvailable: plusAvailable];
}

- initWithAuthzid: (OFString*)authzid
	  authcid: (OFString*)authcid
	 password: (OFString*)password
       connection: (XMPPConnection*)connection
- initWithAuthzid: (OFString *)authzid
	  authcid: (OFString *)authcid
	 password: (OFString *)password
       connection: (XMPPConnection *)connection
	     hash: (Class)hash
    plusAvailable: (bool)plusAvailable
{
	self = [super initWithAuthzid: authzid
			      authcid: authcid
			     password: password];

105
106
107
108
109
110
111
112

113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

147
148
149
150
151
152
153
120
121
122
123
124
125
126

127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143

144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160

161
162
163
164
165
166
167
168







-
+
















-
+
















-
+







	[_serverSignature release];
	[_cNonce release];
	[_connection release];

	[super dealloc];
}

- (void)setAuthzid: (OFString*)authzid
- (void)setAuthzid: (OFString *)authzid
{
	OFString *old = _authzid;

	if (authzid) {
		OFMutableString *new = [[authzid mutableCopy] autorelease];
		[new replaceOccurrencesOfString: @"="
				     withString: @"=3D"];
		[new replaceOccurrencesOfString: @","
				     withString: @"=2C"];
		_authzid = [new retain];
	} else
		_authzid = nil;

	[old release];
}

- (void)setAuthcid: (OFString*)authcid
- (void)setAuthcid: (OFString *)authcid
{
	OFString *old = _authcid;

	if (authcid) {
		OFMutableString *new = [[authcid mutableCopy] autorelease];
		[new replaceOccurrencesOfString: @"="
				     withString: @"=3D"];
		[new replaceOccurrencesOfString: @","
				     withString: @"=2C"];
		_authcid = [new retain];
	} else
		_authcid = nil;

	[old release];
}

- (OFDataArray*)initialMessage
- (OFDataArray *)initialMessage
{
	OFDataArray *ret = [OFDataArray dataArray];

	/* New authentication attempt, reset status */
	[_cNonce release];
	_cNonce = nil;
	[_GS2Header release];
176
177
178
179
180
181
182
183

184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199

200
201
202
203
204
205
206
191
192
193
194
195
196
197

198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

214
215
216
217
218
219
220
221







-
+















-
+








	[ret addItems: [_clientFirstMessageBare UTF8String]
		count: [_clientFirstMessageBare UTF8StringLength]];

	return ret;
}

- (OFDataArray*)continueWithData: (OFDataArray*)data
- (OFDataArray *)continueWithData: (OFDataArray *)data
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	OFDataArray *ret;

	if (!_serverSignature)
		ret = [self XMPP_parseServerFirstMessage: data];
	else
		ret = [self XMPP_parseServerFinalMessage: data];

	[ret retain];
	[pool release];

	return [ret autorelease];
}

- (OFDataArray*)XMPP_parseServerFirstMessage: (OFDataArray*)data
- (OFDataArray *)XMPP_parseServerFirstMessage: (OFDataArray *)data
{
	size_t i;
	const uint8_t *clientKey, *serverKey, *clientSignature;
	intmax_t iterCount = 0;
	id <OFCryptoHash> hash;
	OFDataArray *ret, *authMessage, *tmpArray, *salt = nil, *saltedPassword;
	OFString *tmpString, *sNonce = nil;
249
250
251
252
253
254
255
256


257
258
259
260
261
262
263
264
265
266
267
268
269
270

271
272
273
274
275
276
277
278
279







-
+
+







		@throw [OFInvalidServerReplyException exception];

	// Add c=<base64(GS2Header+channelBindingData)>
	tmpArray = [OFDataArray dataArray];
	[tmpArray addItems: [_GS2Header UTF8String]
		     count: [_GS2Header UTF8StringLength]];
	if (_plusAvailable && [_connection encrypted]) {
		OFDataArray *channelBinding = [((SSLSocket*)[_connection socket])
		OFDataArray *channelBinding =
		    [((SSLSocket *)[_connection socket])
		    channelBindingDataWithType: @"tls-unique"];
		[tmpArray addItems: [channelBinding items]
			     count: [channelBinding count]];
	}
	tmpString = [tmpArray stringByBase64Encoding];
	[ret addItems: "c="
		count: 2];
308
309
310
311
312
313
314
315

316
317
318
319
320
321
322
324
325
326
327
328
329
330

331
332
333
334
335
336
337
338







-
+







	clientKey = [self XMPP_HMACWithKey: saltedPassword
				      data: tmpArray];

	/*
	 * IETF RFC 5802:
	 * StoredKey := H(ClientKey)
	 */
	[hash updateWithBuffer: (void*) clientKey
	[hash updateWithBuffer: (void *)clientKey
			length: [_hashType digestSize]];
	tmpArray = [OFDataArray dataArray];
	[tmpArray addItems: [hash digest]
		     count: [_hashType digestSize]];

	/*
	 * IETF RFC 5802:
364
365
366
367
368
369
370
371

372
373
374
375
376
377
378
380
381
382
383
384
385
386

387
388
389
390
391
392
393
394







-
+







	tmpString = [tmpArray stringByBase64Encoding];
	[ret addItems: [tmpString UTF8String]
		count: [tmpString UTF8StringLength]];

	return ret;
}

- (OFDataArray*)XMPP_parseServerFinalMessage: (OFDataArray*)data
- (OFDataArray *)XMPP_parseServerFinalMessage: (OFDataArray *)data
{
	OFString *mess, *value;

	/*
	 * server-final-message already received,
	 * we were just waiting for the last word from the server
	 */
394
395
396
397
398
399
400
401

402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417

418
419
420
421
422
423


424
425
426
427
428
429
430
410
411
412
413
414
415
416

417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432

433
434
435
436
437


438
439
440
441
442
443
444
445
446







-
+















-
+




-
-
+
+







	} else
		@throw [XMPPAuthFailedException exceptionWithConnection: nil
								 reason: value];

	return nil;
}

- (OFString*)XMPP_genNonce
- (OFString *)XMPP_genNonce
{
	uint8_t buf[64];
	size_t i;

	assert(RAND_pseudo_bytes(buf, 64) >= 0);

	for (i = 0; i < 64; i++) {
		// Restrict salt to printable range, but do not include '~'...
		buf[i] = (buf[i] % ('~' - '!')) + '!';

		// ...so we can use it to replace ','
		if (buf[i] == ',')
			buf[i] = '~';
	}

	return [OFString stringWithCString: (char*)buf
	return [OFString stringWithCString: (char *)buf
				  encoding: OF_STRING_ENCODING_ASCII
				    length: 64];
}

- (const uint8_t*)XMPP_HMACWithKey: (OFDataArray*)key
			data: (OFDataArray*)data
- (const uint8_t *)XMPP_HMACWithKey: (OFDataArray *)key
			       data: (OFDataArray *)data
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	OFDataArray *k = [OFDataArray dataArray];
	size_t i, kSize, blockSize = [_hashType blockSize];
	uint8_t *kI = NULL, *kO = NULL;
	id <OFCryptoHash> hashI, hashO;

449
450
451
452
453
454
455
456

457
458
459
460
461
462

463
464

465
466
467
468
469
470
471
472
473
474
475
476
477
478
479



480
481
482
483
484
485
486
465
466
467
468
469
470
471

472
473
474
475
476
477

478
479

480
481
482
483
484
485
486
487
488
489
490
491
492



493
494
495
496
497
498
499
500
501
502







-
+





-
+

-
+












-
-
-
+
+
+








		for (i = 0; i < blockSize; i++) {
			kI[i] ^= HMAC_IPAD;
			kO[i] ^= HMAC_OPAD;
		}

		hashI = [[[_hashType alloc] init] autorelease];
		[hashI updateWithBuffer: (char*)kI
		[hashI updateWithBuffer: (char *)kI
				 length: blockSize];
		[hashI updateWithBuffer: [data items]
				 length: [data itemSize] * [data count]];

		hashO = [[[_hashType alloc] init] autorelease];
		[hashO updateWithBuffer: (char*)kO
		[hashO updateWithBuffer: (char *)kO
				 length: blockSize];
		[hashO updateWithBuffer: (char*)[hashI digest]
		[hashO updateWithBuffer: (char *)[hashI digest]
				 length: [_hashType digestSize]];
	} @finally {
		[self freeMemory: kI];
		[self freeMemory: kO];
	}

	[hashO retain];
	[pool release];

	return [[hashO autorelease] digest];
}

- (OFDataArray*)XMPP_hiWithData: (OFDataArray*)str
			   salt: (OFDataArray*)salt
		 iterationCount: (intmax_t)i
- (OFDataArray *)XMPP_hiWithData: (OFDataArray *)str
			    salt: (OFDataArray *)salt
		  iterationCount: (intmax_t)i
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	size_t digestSize = [_hashType digestSize];
	uint8_t *result = NULL;
	const uint8_t *u, *uOld;
	intmax_t j, k;
	OFDataArray *salty, *tmp, *ret;

Modified src/XMPPSRVLookup.h from [f29224bbe8] to [c9768d0ace].

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


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







+
+














-
+




-
+


+



-
+











-
+

-
-
+
+
+
-
-
-










+
-
+

+
+
 */

#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>

#import <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@interface XMPPSRVEntry: OFObject
{
	uint16_t _priority;
	uint16_t _weight;
	uint32_t _accumulatedWeight;
	uint16_t _port;
	OFString *_target;
}

@property (readonly) uint16_t priority;
@property (readonly) uint16_t weight;
@property uint32_t accumulatedWeight;
@property (readonly) uint16_t port;
@property (readonly, copy) OFString *target;
@property (readonly, nonatomic) OFString *target;

+ (instancetype)entryWithPriority: (uint16_t)priority
			   weight: (uint16_t)weight
			     port: (uint16_t)port
			   target: (OFString*)target;
			   target: (OFString *)target;
+ (instancetype)entryWithResourceRecord: (ns_rr)resourceRecord
				 handle: (ns_msg)handle;
- init OF_UNAVAILABLE;
- initWithPriority: (uint16_t)priority
	    weight: (uint16_t)weight
	      port: (uint16_t)port
	    target: (OFString*)target;
	    target: (OFString *)target;
- initWithResourceRecord: (ns_rr)resourceRecord
		  handle: (ns_msg)handle;
@end

@interface XMPPSRVLookup: OFObject <OFEnumerating>
{
	OFString *_domain;
	struct __res_state _resState;
	OFList *_list;
}

@property (readonly, copy) OFString *domain;
@property (readonly, nonatomic) OFString *domain;

+ (instancetype)lookupWithDomain: (OFString*)domain;
- initWithDomain: (OFString*)domain;
+ (instancetype)lookupWithDomain: (OFString *)domain;
- init OF_UNAVAILABLE;
- initWithDomain: (OFString *)domain;

- (void)XMPP_lookup;
- (void)XMPP_addEntry: (XMPPSRVEntry*)item;
@end

@interface XMPPSRVEnumerator: OFEnumerator
{
	OFList *_list;
	of_list_object_t *_listIter;
	OFList *_subListCopy;
	bool _done;
}

- init OF_UNAVAILABLE;
- initWithList: (OFList*)list;
- initWithList: (OFList *)list;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPSRVLookup.m from [b57e70e54d] to [e13fa9dc89].

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
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







+
+
+
+
+
+
+
+
+









-
+
















-
-
-
-
-
-
-
+
-





-
+







#include <netdb.h>
#include <sys/types.h>
#include <openssl/rand.h>

#import "XMPPSRVLookup.h"

#import <ObjFW/OFLocalization.h>

OF_ASSUME_NONNULL_BEGIN

@interface XMPPSRVLookup ()
- (void)XMPP_lookup;
- (void)XMPP_addEntry: (XMPPSRVEntry *)item;
@end

OF_ASSUME_NONNULL_END

@implementation XMPPSRVEntry
@synthesize priority = _priority, weight = _weight;
@synthesize accumulatedWeight = _accumulatedWeight, port = _port;
@synthesize target = _target;

+ (instancetype)entryWithPriority: (uint16_t)priority
			   weight: (uint16_t)weight
			     port: (uint16_t)port
			   target: (OFString*)target
			   target: (OFString *)target
{
	return [[[self alloc] initWithPriority: priority
					weight: weight
					  port: port
					target: target] autorelease];
}

+ (instancetype)entryWithResourceRecord: (ns_rr)resourceRecord
				 handle: (ns_msg)handle
{
	return [[[self alloc] initWithResourceRecord: resourceRecord
					      handle: handle] autorelease];
}

- init
{
	@try {
		[self doesNotRecognizeSelector: _cmd];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	OF_INVALID_INIT_METHOD
	abort();
}

- initWithPriority: (uint16_t)priority
	    weight: (uint16_t)weight
	      port: (uint16_t)port
	    target: (OFString*)target
	    target: (OFString *)target
{
	self = [super init];

	@try {
		_priority = priority;
		_weight = weight;
		_port = port;
100
101
102
103
104
105
106
107

108
109
110
111
112
113

114
115
116
117
118
119
120
102
103
104
105
106
107
108

109
110
111
112
113
114

115
116
117
118
119
120
121
122







-
+





-
+







{
	self = [super init];

	@try {
		const uint16_t *rdata;
		char buffer[NS_MAXDNAME];

		rdata = (const uint16_t*)(void*)ns_rr_rdata(resourceRecord);
		rdata = (const uint16_t *)(void *)ns_rr_rdata(resourceRecord);
		_priority = ntohs(rdata[0]);
		_weight = ntohs(rdata[1]);
		_port = ntohs(rdata[2]);

		if (dn_expand(ns_msg_base(handle), ns_msg_end(handle),
		    (uint8_t*)&rdata[3], buffer, NS_MAXDNAME) < 1)
		    (uint8_t *)&rdata[3], buffer, NS_MAXDNAME) < 1)
			@throw [OFInitializationFailedException
			    exceptionWithClass: [self class]];

		_target = [[OFString alloc]
		    initWithCString: buffer
			   encoding: [OFLocalization encoding]];
	} @catch (id e) {
128
129
130
131
132
133
134
135

136
137
138
139
140
141
142
143
144
145
146

147
148
149
150





151

152
153
154
155
156
157
158
130
131
132
133
134
135
136

137
138
139
140
141
142
143
144
145
146
147

148
149
150
151
152
153
154
155
156
157

158
159
160
161
162
163
164
165







-
+










-
+




+
+
+
+
+
-
+







- (void)dealloc
{
	[_target release];

	[super dealloc];
}

- (OFString*)description
- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"<%@ priority: %" PRIu16 @", weight: %" PRIu16 @", target: %@:%"
	    PRIu16 @">", [self class], _priority, _weight, _target, _port];
}
@end

@implementation XMPPSRVLookup
@synthesize domain = _domain;

+ (instancetype)lookupWithDomain: (OFString*)domain
+ (instancetype)lookupWithDomain: (OFString *)domain
{
	return [[[self alloc] initWithDomain: domain] autorelease];
}

- init
{
	OF_INVALID_INIT_METHOD
}

- initWithDomain: (OFString*)domain
- initWithDomain: (OFString *)domain
{
	self = [super init];

	@try {
		_list = [[OFList alloc] init];
		_domain = [domain copy];

227
228
229
230
231
232
233
234

235
236
237
238
239
240
241
234
235
236
237
238
239
240

241
242
243
244
245
246
247
248







-
+







		res_ndestroy(&_resState);
#endif
	}

	[pool release];
}

- (void)XMPP_addEntry: (XMPPSRVEntry*)entry
- (void)XMPP_addEntry: (XMPPSRVEntry *)entry
{
	OFAutoreleasePool *pool;
	OFList *subList;
	of_list_object_t *iter;

	/* Look if there already is a list with the priority */
	for (iter = [_list firstListObject]; iter != NULL; iter = iter->next) {
269
270
271
272
273
274
275
276

277
278
279
280
281
282





283

284
285
286
287
288
289
290
276
277
278
279
280
281
282

283
284
285
286
287
288
289
290
291
292
293
294

295
296
297
298
299
300
301
302







-
+






+
+
+
+
+
-
+







		   beforeListObject: iter];
	else
		[_list appendObject: subList];

	[pool release];
}

- (OFEnumerator*)objectEnumerator
- (OFEnumerator *)objectEnumerator
{
	return [[[XMPPSRVEnumerator alloc] initWithList: _list] autorelease];
}
@end

@implementation XMPPSRVEnumerator
- init
{
	OF_INVALID_INIT_METHOD
}

- initWithList: (OFList*)list
- initWithList: (OFList *)list
{
	self = [super init];

	@try {
		_list = [list copy];
	} @catch (id e) {
		[self release];
317
318
319
320
321
322
323
324

325
326
327
328
329
330
331
329
330
331
332
333
334
335

336
337
338
339
340
341
342
343







-
+







		totalWeight += [iter->object weight];
		[iter->object setAccumulatedWeight: totalWeight];
	}

	if ([_subListCopy count] > 0)  {
		uint32_t randomWeight;

		RAND_pseudo_bytes((uint8_t*)&randomWeight, sizeof(uint32_t));
		RAND_pseudo_bytes((uint8_t *)&randomWeight, sizeof(uint32_t));
		randomWeight %= (totalWeight + 1);

		for (iter = [_subListCopy firstListObject]; iter != NULL;
		     iter = iter->next) {
			if ([iter->object accumulatedWeight] >= randomWeight) {
				ret = [[iter->object retain] autorelease];

Modified src/XMPPStanza.h from [7ce6b98454] to [a0d35df9af].

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
102
103
104

105
106
107
108
109
110
111
112
113
114
115


116
117
118
119
120
121
122
123
124
125
126


127
128
129
130
131
132
133
134
135
136
137
138
139



140
141
142
143
144
145
146
147

148


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
102
103
104
105
106
107
108
109
110
111
112
113
114
115

116
117
118
119
120
121
122
123
124
125


126
127
128
129
130
131
132
133
134
135
136


137
138
139
140
141
142
143
144
145
146
147
148



149
150
151
152
153
154
155
156
157
158

159
160
161
162







+
+












-
+

-
+

-
+

-
+

-
+







-
+









-
-
+
+








-
-
+
+










-
-
-
+
+
+







-
+
+
+
+
+
+
+
+
+
+
+







-
+









-
-
+
+









-
-
+
+










-
-
-
+
+
+







-
+

+
+
 * 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 <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@class XMPPJID;

/**
 * \brief A class describing an XMPP Stanza.
 */
@interface XMPPStanza: OFXMLElement
{
	XMPPJID *_from, *_to;
	OFString *_type, *_ID, *_language;
}

/// \brief The value of the stanza's from attribute
@property (copy) XMPPJID *from;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) XMPPJID *from;
/// \brief The value of the stanza's to attribute
@property (copy) XMPPJID *to;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) XMPPJID *to;
/// \brief The value of the stanza's type attribute
@property (copy) OFString *type;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *type;
/// \brief The value of the stanza's id attribute
@property (copy) OFString *ID;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *ID;
/// \brief The stanza's xml:lang
@property (copy) OFString *language;
@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *language;

/**
 * \brief Creates a new autoreleased XMPPStanza with the specified name.
 *
 * \param name The stanza's name (one of iq, message or presence)
 * \return A new autoreleased XMPPStanza
 */
+ (instancetype)stanzaWithName: (OFString*)name;
+ (instancetype)stanzaWithName: (OFString *)name;

/**
 * \brief Creates a new autoreleased XMPPStanza with the specified name and
 *	  type.
 *
 * \param name The stanza's name (one of iq, message or presence)
 * \param type The value for the stanza's type attribute
 * \return A new autoreleased XMPPStanza
 */
+ (instancetype)stanzaWithName: (OFString*)name
			  type: (OFString*)type;
+ (instancetype)stanzaWithName: (OFString *)name
			  type: (nullable OFString *)type;

/**
 * \brief Creates a new autoreleased XMPPStanza with the specified name and ID.
 *
 * \param name The stanza's name (one of iq, message or presence)
 * \param ID The value for the stanza's id attribute
 * \return A new autoreleased XMPPStanza
 */
+ (instancetype)stanzaWithName: (OFString*)name
			    ID: (OFString*)ID;
+ (instancetype)stanzaWithName: (OFString *)name
			    ID: (nullable OFString *)ID;

/**
 * \brief Creates a new autoreleased XMPPStanza with the specified name, type
 *	  and ID.
 *
 * \param name The stanza's name (one of iq, message or presence)
 * \param type The value for the stanza's type attribute
 * \param ID The value for the stanza's id attribute
 * \return A new autoreleased XMPPStanza
 */
+ (instancetype)stanzaWithName: (OFString*)name
			  type: (OFString*)type
			    ID: (OFString*)ID;
+ (instancetype)stanzaWithName: (OFString *)name
			  type: (nullable OFString *)type
			    ID: (nullable OFString *)ID;

/**
 * \brief Creates a new autoreleased XMPPStanza from an OFXMLElement.
 *
 * \param element The element to base the XMPPStanza on
 * \return A new autoreleased XMPPStanza
 */
+ (instancetype)stanzaWithElement: (OFXMLElement*)element;
+ (instancetype)stanzaWithElement: (OFXMLElement *)element;

- initWithName: (OFString *)name
   stringValue: (nullable OFString *)stringValue OF_UNAVAILABLE;
- initWithName: (OFString *)name
     namespace: (nullable OFString *)namespace OF_UNAVAILABLE;
- initWithName: (OFString *)name
     namespace: (nullable OFString *)namespace
   stringValue: (nullable OFString *)stringValue OF_UNAVAILABLE;
- initWithXMLString: (OFString *)string OF_UNAVAILABLE;
- initWithFile: (OFString *)path OF_UNAVAILABLE;

/**
 * \brief Initializes an already allocated XMPPStanza with the specified name.
 *
 * \param name The stanza's name (one of iq, message or presence)
 * \return A initialized XMPPStanza
 */
- initWithName: (OFString*)name;
- initWithName: (OFString *)name;

/**
 * \brief Initializes an already allocated XMPPStanza with the specified name
 *	  and type.
 *
 * \param name The stanza's name (one of iq, message or presence)
 * \param type The value for the stanza's type attribute
 * \return A initialized XMPPStanza
 */
- initWithName: (OFString*)name
	  type: (OFString*)type;
- initWithName: (OFString *)name
	  type: (nullable OFString *)type;

/**
 * \brief Initializes an already allocated XMPPStanza with the specified name
 *	  and ID.
 *
 * \param name The stanza's name (one of iq, message or presence)
 * \param ID The value for the stanza's id attribute
 * \return A initialized XMPPStanza
 */
- initWithName: (OFString*)name
	    ID: (OFString*)ID;
- initWithName: (OFString *)name
	    ID: (nullable OFString *)ID;

/**
 * \brief Initializes an already allocated XMPPStanza with the specified name,
 *	  type and ID.
 *
 * \param name The stanza's name (one of iq, message or presence)
 * \param type The value for the stanza's type attribute
 * \param ID The value for the stanza's id attribute
 * \return A initialized XMPPStanza
 */
- initWithName: (OFString*)name
	  type: (OFString*)type
	    ID: (OFString*)ID;
- initWithName: (OFString *)name
	  type: (nullable OFString *)type
	    ID: (nullable OFString *)ID;

/**
 * \brief Initializes an already allocated XMPPStanza based on a OFXMLElement.
 *
 * \param element The element to base the XMPPStanza on
 * \return A initialized XMPPStanza
 */
- initWithElement: (OFXMLElement*)element;
- initWithElement: (OFXMLElement *)element;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPStanza.m from [62c85c1076] to [26c7c49d18].

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
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
102
103
104
105
106
107
108



109
110
111
112
113
114
115
116
117
118







+
+
+
-
+




-
-
+
+





-
-
+
+





-
-
-
+
+
+






-
+




-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






-
-
+
+






-
-
+
+






-
-
-
+
+
+







#endif

#import "XMPPStanza.h"
#import "XMPPJID.h"
#import "namespaces.h"

@implementation XMPPStanza
@synthesize from = _from, to = _to, type = _type, ID = _ID;
@synthesize language = _language;

+ (instancetype)stanzaWithName: (OFString*)name
+ (instancetype)stanzaWithName: (OFString *)name
{
	return [[[self alloc] initWithName: name] autorelease];
}

+ (instancetype)stanzaWithName: (OFString*)name
			  type: (OFString*)type
+ (instancetype)stanzaWithName: (OFString *)name
			  type: (OFString *)type
{
	return [[[self alloc] initWithName: name
				      type: type] autorelease];
}

+ (instancetype)stanzaWithName: (OFString*)name
			    ID: (OFString*)ID
+ (instancetype)stanzaWithName: (OFString *)name
			    ID: (OFString *)ID
{
	return [[[self alloc] initWithName: name
					ID: ID] autorelease];
}

+ (instancetype)stanzaWithName: (OFString*)name
			  type: (OFString*)type
			    ID: (OFString*)ID
+ (instancetype)stanzaWithName: (OFString *)name
			  type: (OFString *)type
			    ID: (OFString *)ID
{
	return [[[self alloc] initWithName: name
				      type: type
					ID: ID] autorelease];
}

+ (instancetype)stanzaWithElement: (OFXMLElement*)element
+ (instancetype)stanzaWithElement: (OFXMLElement *)element
{
	return [[[self alloc] initWithElement: element] autorelease];
}

- initWithName: (OFString*)name
- initWithName: (OFString *)name
     namespace: (nullable OFString *)namespace
   stringValue: (nullable OFString *)stringValue
{
	OF_INVALID_INIT_METHOD
}

- initWithXMLString: (OFString *)string
{
	OF_INVALID_INIT_METHOD
}

- initWithFile: (OFString *)path
{
	OF_INVALID_INIT_METHOD
}

- initWithName: (OFString *)name
{
	return [self initWithName: name
			     type: nil
			       ID: nil];
}

- initWithName: (OFString*)name
	  type: (OFString*)type
- initWithName: (OFString *)name
	  type: (OFString *)type
{
	return [self initWithName: name
			     type: type
			       ID: nil];
}

- initWithName: (OFString*)name
	    ID: (OFString*)ID
- initWithName: (OFString *)name
	    ID: (OFString *)ID
{
	return [self initWithName: name
			     type: nil
			       ID: ID];
}

- initWithName: (OFString*)name
	  type: (OFString*)type
	    ID: (OFString*)ID
- initWithName: (OFString *)name
	  type: (OFString *)type
	    ID: (OFString *)ID
{
	self = [super initWithName: name
			 namespace: XMPP_NS_CLIENT];

	@try {
		if (![name isEqual: @"iq"] && ![name isEqual: @"message"] &&
		    ![name isEqual: @"presence"])
111
112
113
114
115
116
117
118

119
120
121
122
123
124
125
131
132
133
134
135
136
137

138
139
140
141
142
143
144
145







-
+







		[self release];
		@throw e;
	}

	return self;
}

- initWithElement: (OFXMLElement*)element
- initWithElement: (OFXMLElement *)element
{
	self = [super initWithElement: element];

	@try {
		OFXMLAttribute *attribute;

		if ((attribute = [element attributeForName: @"from"]))
149
150
151
152
153
154
155
156

157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174

175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192

193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210

211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228

229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
169
170
171
172
173
174
175

176
177
178
179
180
181
182
183
184
185
186
187
188






189
190
191
192
193
194
195
196
197
198
199
200
201






202
203
204
205
206
207
208
209
210
211
212
213
214






215
216
217
218
219
220
221
222
223
224
225
226
227






228
229
230
231
232
233
234
235
236
237
238
239
240
241
242





243







-
+












-
-
-
-
-
-
+












-
-
-
-
-
-
+












-
-
-
-
-
-
+












-
-
-
-
-
-
+














-
-
-
-
-

	[_to release];
	[_type release];
	[_ID release];

	[super dealloc];
}

- (void)setFrom: (XMPPJID*)from
- (void)setFrom: (XMPPJID *)from
{
	XMPPJID *old = _from;
	_from = [from copy];
	[old release];

	[self removeAttributeForName: @"from"];

	if (from != nil)
		[self addAttributeWithName: @"from"
			       stringValue: [from fullJID]];
}

- (XMPPJID*)from
{
	return [[_from copy] autorelease];
}

- (void)setTo: (XMPPJID*)to
- (void)setTo: (XMPPJID *)to
{
	XMPPJID *old = _to;
	_to = [to copy];
	[old release];

	[self removeAttributeForName: @"to"];

	if (to != nil)
		[self addAttributeWithName: @"to"
			       stringValue: [to fullJID]];
}

- (XMPPJID*)to
{
	return [[_to copy] autorelease];
}

- (void)setType: (OFString*)type
- (void)setType: (OFString *)type
{
	OFString *old = _type;
	_type = [type copy];
	[old release];

	[self removeAttributeForName: @"type"];

	if (type != nil)
		[self addAttributeWithName: @"type"
			       stringValue: type];
}

- (OFString*)type
{
	return [[_type copy] autorelease];
}

- (void)setID: (OFString*)ID
- (void)setID: (OFString *)ID
{
	OFString *old = _ID;
	_ID = [ID copy];
	[old release];

	[self removeAttributeForName: @"id"];

	if (ID != nil)
		[self addAttributeWithName: @"id"
			       stringValue: ID];
}

- (OFString*)ID
{
	return [[_ID copy] autorelease];
}

- (void)setLanguage: (OFString*)language
- (void)setLanguage: (OFString *)language
{
	OFString *old = _language;
	_language = [language copy];
	[old release];

	[self removeAttributeForName: @"lang"
			   namespace: @"http://www.w3.org/XML/1998/namespace"];

	if (language != nil)
		[self addAttributeWithName: @"lang"
				 namespace: @"http://www.w3.org/XML/1998/"
					    @"namespace"
			       stringValue: language];
}

- (OFString*)language
{
	return [[_language copy] autorelease];
}
@end

Modified src/XMPPStorage.h from [f2a91237cc] to [1c089b9216].

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


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







+
+






-
-
-
+
+
+

-
-
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

+
+
 * 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 <ObjFW/OFObject.h>

OF_ASSUME_NONNULL_BEGIN

@class OFString;
@class OFArray;
@class OFDictionary;

@protocol XMPPStorage <OFObject>
- (void)save;
- (void)setStringValue: (OFString*)string
	       forPath: (OFString*)path;
- (OFString*)stringValueForPath: (OFString*)path;
- (void)setStringValue: (nullable OFString *)string
	       forPath: (OFString *)path;
- (nullable OFString *)stringValueForPath: (OFString *)path;
- (void)setBooleanValue: (bool)boolean
		forPath: (OFString*)path;
- (bool)booleanValueForPath: (OFString*)path;
		forPath: (OFString *)path;
- (bool)booleanValueForPath: (OFString *)path;
- (void)setIntegerValue: (intmax_t)integer
		forPath: (OFString*)path;
- (intmax_t)integerValueForPath: (OFString*)path;
- (void)setArray: (OFArray*)array
	 forPath: (OFString*)path;
- (OFArray*)arrayForPath: (OFString*)path;
- (void)setDictionary: (OFDictionary*)dictionary
	      forPath: (OFString*)path;
- (OFDictionary*)dictionaryForPath: (OFString*)path;
		forPath: (OFString *)path;
- (intmax_t)integerValueForPath: (OFString *)path;
- (void)setArray: (nullable OFArray *)array
	 forPath: (OFString *)path;
- (nullable OFArray *)arrayForPath: (OFString *)path;
- (void)setDictionary: (nullable OFDictionary *)dictionary
	      forPath: (OFString *)path;
- (nullable OFDictionary *)dictionaryForPath: (OFString *)path;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPStreamManagement.h from [dbbf14c9a5] to [eba4bbe440].

18
19
20
21
22
23
24


25
26
27
28
29
30
31

32

33


18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

35
36
37
38







+
+







+
-
+

+
+
 * 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"

OF_ASSUME_NONNULL_BEGIN

@interface XMPPStreamManagement: OFObject <XMPPConnectionDelegate>
{
	XMPPConnection *_connection;
	uint32_t _receivedCount;
}

- init OF_UNAVAILABLE;
- initWithConnection: (XMPPConnection*)connection;
- initWithConnection: (XMPPConnection *)connection;
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPStreamManagement.m from [9dc52c523a] to [57b85a948f].

22
23
24
25
26
27
28





29

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

34
35
36
37
38
39
40
41







+
+
+
+
+
-
+








#include <inttypes.h>

#import "XMPPStreamManagement.h"
#import "namespaces.h"

@implementation XMPPStreamManagement
- init
{
	OF_INVALID_INIT_METHOD
}

- initWithConnection: (XMPPConnection*)connection
- initWithConnection: (XMPPConnection *)connection
{
	self = [super init];

	@try {
		_connection = connection;
		[_connection addDelegate: self];
	} @catch (id e) {
44
45
46
47
48
49
50
51
52


53
54
55
56
57
58
59
49
50
51
52
53
54
55


56
57
58
59
60
61
62
63
64







-
-
+
+







- (void)dealloc
{
	[_connection removeDelegate: self];

	[super dealloc];
}

- (void)connection: (XMPPConnection*)connection
 didReceiveElement: (OFXMLElement*)element
- (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;
81
82
83
84
85
86
87
88
89


90
91
92
93
94
95


96
97
98
99
100
101
102
86
87
88
89
90
91
92


93
94
95
96
97
98


99
100
101
102
103
104
105
106
107







-
-
+
+




-
-
+
+







	    ([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
    didSendElement: (OFXMLElement *)element
{
}
*/

- (void)connection: (XMPPConnection*)connection
     wasBoundToJID: (XMPPJID*)JID
- (void)connection: (XMPPConnection *)connection
     wasBoundToJID: (XMPPJID *)JID
{
	if ([connection supportsStreamManagement])
		[connection sendStanza:
		    [OFXMLElement elementWithName: @"enable"
					namespace: XMPP_NS_SM]];
}
@end

Modified src/XMPPXMLElementBuilder.h from [e6038a457f] to [60ea5de24d].

17
18
19
20
21
22
23


24
25
26


17
18
19
20
21
22
23
24
25
26
27
28
29
30







+
+



+
+
 * 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 <ObjFW/OFXMLElementBuilder.h>

OF_ASSUME_NONNULL_BEGIN

@interface XMPPXMLElementBuilder: OFXMLElementBuilder
@end

OF_ASSUME_NONNULL_END

Modified src/XMPPXMLElementBuilder.m from [c3e2053f34] to [757efe5b8a].

25
26
27
28
29
30
31
32
33


34
35
36
37
38
39


40
41
42
43
25
26
27
28
29
30
31


32
33
34
35
36
37


38
39
40
41
42
43







-
-
+
+




-
-
+
+




#endif

#import "XMPPXMLElementBuilder.h"

#import <ObjFW/OFMalformedXMLException.h>

@implementation XMPPXMLElementBuilder
-		 (void)parser: (OFXMLParser*)parser
  foundProcessingInstructions: (OFString*)pi
-		 (void)parser: (OFXMLParser *)parser
  foundProcessingInstructions: (OFString *)pi
{
	@throw [OFMalformedXMLException exception];
}

- (void)parser: (OFXMLParser*)parser
  foundComment: (OFString*)comment
- (void)parser: (OFXMLParser *)parser
  foundComment: (OFString *)comment
{
	@throw [OFMalformedXMLException exception];
}
@end

Modified tests/test.m from [1e6a6151cf] to [a93ba6280b].

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
34
35
36
37
38
39
40

41

42
43
44
45
46
47
48







-

-







#import "XMPPMessage.h"
#import "XMPPPresence.h"
#import "XMPPRoster.h"
#import "XMPPStreamManagement.h"
#import "XMPPFileStorage.h"

@interface AppDelegate: OFObject
#ifdef OF_HAVE_OPTIONAL_PROTOCOLS
    <OFApplicationDelegate, XMPPConnectionDelegate, XMPPRosterDelegate>
#endif
{
	XMPPConnection *conn;
	XMPPRoster *roster;
}
@end

OF_APPLICATION_DELEGATE(AppDelegate)
127
128
129
130
131
132
133
134
135


136
137
138
139
140
141


142
143
144
145
146

147
148
149
150
151
152


153
154
155
156
157
158
159
125
126
127
128
129
130
131


132
133
134
135
136
137


138
139
140
141
142
143

144
145
146
147
148


149
150
151
152
153
154
155
156
157







-
-
+
+




-
-
+
+




-
+




-
-
+
+







	[conn setUsername: [arguments objectAtIndex: 1]];
	[conn setPassword: [arguments objectAtIndex: 2]];
	[conn setResource: @"ObjXMPP"];

	[conn asyncConnectAndHandle];
}

-  (void)connection: (XMPPConnection*)conn
  didReceiveElement: (OFXMLElement*)element
-  (void)connection: (XMPPConnection *)conn
  didReceiveElement: (OFXMLElement *)element
{
	of_log(@"In:  %@", element);
}

- (void)connection: (XMPPConnection*)conn
    didSendElement: (OFXMLElement*)element
- (void)connection: (XMPPConnection *)conn
    didSendElement: (OFXMLElement *)element
{
	of_log(@"Out: %@", element);
}

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

- (void)connection: (XMPPConnection*)conn_
     wasBoundToJID: (XMPPJID*)jid
- (void)connection: (XMPPConnection *)conn_
     wasBoundToJID: (XMPPJID *)jid
{
	of_log(@"Bound to JID: %@", [jid fullJID]);
	of_log(@"Supports SM: %@",
	    [conn_ supportsStreamManagement] ? @"true" : @"false");

	XMPPDiscoEntity *discoEntity =
	    [[XMPPDiscoEntity alloc] initWithConnection: conn];
196
197
198
199
200
201
202
203

204
205
206
207
208
209
210
194
195
196
197
198
199
200

201
202
203
204
205
206
207
208







-
+







	[discoEntity addDiscoNode: nodeClueso];
	[discoEntity addDiscoNode: nodeStop];
	[discoEntity addDiscoNode: nodeChicago];

	[roster requestRoster];
}

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

	of_log(@"Got roster: %@", [roster_ rosterItems]);

	pres = [XMPPPresence presence];
	[pres setPriority: [OFNumber numberWithInt8: 10]];
220
221
222
223
224
225
226
227

228
229
230
231
232
233
234
235
236
237
238
239
240
241
242


243
244
245
246
247
248


249
250
251
252
253
254
255
256


257
258
259
260
261
262


263
264
265
266
267

268
269
270
271
272
273

274
275
276
277
218
219
220
221
222
223
224

225
226
227
228
229
230
231
232
233
234
235
236
237
238


239
240
241
242
243
244


245
246
247
248
249
250
251
252


253
254
255
256
257
258


259
260
261
262
263
264

265
266
267
268
269
270

271
272
273
274
275







-
+













-
-
+
+




-
-
+
+






-
-
+
+




-
-
+
+




-
+





-
+




	[conn	   sendIQ: iq
	    callbackBlock: ^ (XMPPConnection *c, XMPPIQ *resp) {
		of_log(@"Ping response: %@", resp);
	}];
#endif
}

- (void)connectionDidUpgradeToTLS: (XMPPConnection*)conn_
- (void)connectionDidUpgradeToTLS: (XMPPConnection *)conn_
{
	OFString *reason;

	if (![conn_ checkCertificateAndGetReason: &reason]) {
		[of_stdout writeString: @"Couldn't verify certificate: "];
		[of_stdout writeFormat: @"%@\n", reason];
		[of_stdout writeString: @"Do you want to continue [y/N]? "];

		if (![[of_stdin readLine] hasPrefix: @"y"])
			[OFApplication terminateWithStatus: 1];
	}
}

-         (void)roster: (XMPPRoster*)roster_
  didReceiveRosterItem: (XMPPRosterItem*)rosterItem
-         (void)roster: (XMPPRoster *)roster_
  didReceiveRosterItem: (XMPPRosterItem *)rosterItem
{
	of_log(@"Got roster push: %@", rosterItem);
}

- (bool)connection: (XMPPConnection*)conn
      didReceiveIQ: (XMPPIQ*)iq
- (bool)connection: (XMPPConnection *)conn
      didReceiveIQ: (XMPPIQ *)iq
{
	of_log(@"IQ: %@", iq);

	return NO;
}

-  (void)connection: (XMPPConnection*)conn
  didReceiveMessage: (XMPPMessage*)msg
-  (void)connection: (XMPPConnection *)conn
  didReceiveMessage: (XMPPMessage *)msg
{
	of_log(@"Message: %@", msg);
}

-   (void)connection: (XMPPConnection*)conn
  didReceivePresence: (XMPPPresence*)pres
-   (void)connection: (XMPPConnection *)conn
  didReceivePresence: (XMPPPresence *)pres
{
	of_log(@"Presence: %@", pres);
}

-  (void)connection: (XMPPConnection*)conn
-  (void)connection: (XMPPConnection *)conn
  didThrowException: (id)e
{
	@throw e;
}

- (void)connectionWasClosed: (XMPPConnection*)conn
- (void)connectionWasClosed: (XMPPConnection *)conn
{
	of_log(@"Connection was closed!");
}
@end