ObjMatrix  Check-in [e597cc80e1]

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: e597cc80e1537bae3544bccd7b55dbdf963bc846184521ac9bd0e2e82aa695f0
User & Date: js on 2021-04-29 00:32:30
Other Links: manifest | tags
Context
2024-07-15
20:31
Adjust to ObjFW changes check-in: 531eb84cf5 user: js tags: trunk
2021-04-29
00:32
Adjust to ObjFW changes check-in: e597cc80e1 user: js tags: trunk
2020-10-31
19:37
Adjust to ObjFW changes check-in: 6b6fe6d802 user: js tags: trunk
Changes

Modified extra.mk.in from [ce5a64be0d] to [6c390c751b].

1
2
3
4
5
6

7
8
9
10
11
12
13
1
2
3
4
5

6
7
8
9
10
11
12
13





-
+







OBJMATRIX_SHARED_LIB = @OBJMATRIX_SHARED_LIB@
OBJMATRIX_STATIC_LIB = @OBJMATRIX_STATIC_LIB@
OBJMATRIX_FRAMEWORK = @OBJMATRIX_FRAMEWORK@
OBJMATRIX_LIB_MAJOR = 0
OBJMATRIX_LIB_MINOR = 0
OBJMATRIX_LIB_MAJOR_MINOR = ${OBJMATRIX_LIB_MAJOR_MINOR}.${OBJMATRIX_LIB_MINOR}
OBJMATRIX_LIB_MAJOR_MINOR = ${OBJMATRIX_LIB_MAJOR}.${OBJMATRIX_LIB_MINOR}

EXCEPTIONS_A = @EXCEPTIONS_A@
EXCEPTIONS_EXCEPTIONS_A = @EXCEPTIONS_EXCEPTIONS_A@
EXCEPTIONS_EXCEPTIONS_LIB_A = @EXCEPTIONS_EXCEPTIONS_LIB_A@
EXCEPTIONS_LIB_A = @EXCEPTIONS_LIB_A@
LIBOBJMATRIX_DEP = @LIBOBJMATRIX_DEP@
OBJFW_CONFIG = @OBJFW_CONFIG@

Modified src/MTXClient.h from [4640a34e7e] to [fc371d6d5c].

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







-
+







-
+






-
+







-
-
+
+








-
+








/**
 * @brief A block called when a new login succeeded or failed.
 *
 * @param client If the login succeeded, the newly created client
 * @param exception If the login failed, an exception
 */
typedef void (^mtx_client_login_block_t)(MTXClient *_Nullable client,
typedef void (^MTXClientLoginBlock)(MTXClient *_Nullable client,
    id _Nullable exception);

/**
 * @brief A block called when the response for an operation was received.
 *
 * @param exception `nil` on success, otherwise an exception
 */
typedef void (^mtx_client_response_block_t)(id _Nullable exception);
typedef void (^MTXClientResponseBlock)(id _Nullable exception);

/**
 * @brief A block called when an exception occurred during sync.
 *
 * @param exception The exception which occurred during sync
 */
typedef void (^mtx_sync_exception_handler_block_t)(id exception);
typedef void (^MTXSyncExceptionHandlerBlock)(id exception);

/**
 * @brief A block called when the room list was fetched.
 *
 * @param rooms An array of joined rooms, or nil on error
 * @param exception An exception if fetching the room list failed
 */
typedef void (^mtx_client_room_list_block_t)(
    OFArray<OFString *> *_Nullable rooms, id _Nullable exception);
typedef void (^MTXClientRoomListBlock)(OFArray<OFString *> *_Nullable rooms,
    id _Nullable exception);

/**
 * @brief A block called when a room was joined.
 *
 * @param roomID The room ID that was joined, or nil on error. This can be used
 *		 to get the room ID if a room alias was joined.
 * @param exception An exception if joining the room failed
 */
typedef void (^mtx_client_room_join_block_t)(OFString *_Nullable roomID,
typedef void (^MTXClientRoomJoinBlock)(OFString *_Nullable roomID,
    id _Nullable exception);

/**
 * @brief A class that represents a client.
 */
@interface MTXClient: OFObject
/**
100
101
102
103
104
105
106
107

108
109
110
111
112

113
114
115
116
117
118
119
120
100
101
102
103
104
105
106

107
108
109
110
111

112

113
114
115
116
117
118
119







-
+




-
+
-







@property (readonly, nonatomic) id <MTXStorage> storage;

/**
 * @brief The timeout for sync requests.
 *
 * Defaults to 5 minutes.
 */
@property (nonatomic) of_time_interval_t syncTimeout;
@property (nonatomic) OFTimeInterval syncTimeout;

/**
 * @brief A block to handle exceptions that occurred during sync.
 */
@property (copy, nonatomic)
@property (copy, nonatomic) MTXSyncExceptionHandlerBlock syncExceptionHandler;
    mtx_sync_exception_handler_block_t syncExceptionHandler;

/**
 * @brief Creates a new client with the specified access token on the specified
 *	  homeserver.
 *
 * @param userID The user ID for the client
 * @param deviceID The device ID for the client
138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
137
138
139
140
141
142
143

144
145
146
147
148
149
150
151







-
+







 * @param storage The storage the client should use
 * @param block A block to call once login succeeded or failed
 */
+ (void)logInWithUser: (OFString *)user
	     password: (OFString *)password
	   homeserver: (OFURL *)homeserver
	      storage: (id <MTXStorage>)storage
		block: (mtx_client_login_block_t)block;
		block: (MTXClientLoginBlock)block;

/**
 * @brief Initializes an already allocated client with the specified access
 *	  token on the specified homeserver.
 *
 * @param userID The user ID for the client
 * @param deviceID The device ID for the client
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
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







-
+






-
+







-
+
-







-
+
-










-
+



/**
 * @brief Logs out the device and invalidates the access token.
 *
 * @warning The client can no longer be used after this succeeded!
 *
 * @param block A block to call when logging out succeeded or failed
 */
- (void)logOutWithBlock: (mtx_client_response_block_t)block;
- (void)logOutWithBlock: (MTXClientResponseBlock)block;

/**
 * @brief Fetches the list of joined rooms.
 *
 * @param block A block to call with the list of joined room
 */
- (void)fetchRoomListWithBlock: (mtx_client_room_list_block_t)block;
- (void)fetchRoomListWithBlock: (MTXClientRoomListBlock)block;

/**
 * @brief Joins the specified room.
 *
 * @param room The room to join. Either a room ID or a room alias.
 * @param block A block to call when the room was joined
 */
- (void)joinRoom: (OFString *)room
- (void)joinRoom: (OFString *)room block: (MTXClientRoomJoinBlock)block;
	   block: (mtx_client_room_join_block_t)block;

/**
 * @brief Leaves the specified room.
 *
 * @param roomID The room ID to leave
 * @param block A block to call when the room was left
 */
- (void)leaveRoom: (OFString *)roomID
- (void)leaveRoom: (OFString *)roomID block: (MTXClientResponseBlock)block;
	    block: (mtx_client_response_block_t)block;

/**
 * @brief Sends the specified message to the specified room ID.
 *
 * @param message The message to send
 * @param roomID The room ID to which to send the message
 * @param block A block to call when the message was sent
 */
- (void)sendMessage: (OFString *)message
	     roomID: (OFString *)roomID
	      block: (mtx_client_response_block_t)block;
	      block: (MTXClientResponseBlock)block;
@end

OF_ASSUME_NONNULL_END

Modified src/MTXClient.m from [90a5b4b816] to [6d2f252461].

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







-
+









-
+









-
+







				     storage: storage] autorelease];
}

+ (void)logInWithUser: (OFString *)user
	     password: (OFString *)password
	   homeserver: (OFURL *)homeserver
	      storage: (id <MTXStorage>)storage
		block: (mtx_client_login_block_t)block
		block: (MTXClientLoginBlock)block
{
	void *pool = objc_autoreleasePoolPush();

	validateHomeserver(homeserver);

	MTXRequest *request = [MTXRequest
	    requestWithPath: @"/_matrix/client/r0/login"
		accessToken: nil
		 homeserver: homeserver];
	request.method = OF_HTTP_REQUEST_METHOD_POST;
	request.method = OFHTTPRequestMethodPost;
	request.body = @{
		@"type": @"m.login.password",
		@"identifier": @{
			@"type": @"m.id.user",
			@"user": user
		},
		@"password": password
	};

	[request performWithBlock: ^ (mtx_response_t response, int statusCode,
	[request performWithBlock: ^ (MTXResponse response, int statusCode,
				       id exception) {
		if (exception != nil) {
			block(nil, exception);
			return;
		}

		if (statusCode != 200) {
213
214
215
216
217
218
219
220

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

220
221
222
223
224
225
226
227







-
+







	    requestWithPath: @"/_matrix/client/r0/sync"];
	unsigned long long timeoutMs = _syncTimeout * 1000;
	OFMutableDictionary<OFString *, OFString *> *query =
	    [OFMutableDictionary dictionaryWithObject: @(timeoutMs).stringValue
					       forKey: @"timeout"];
	query[@"since"] = [_storage nextBatchForDeviceID: _deviceID];
	request.query = query;
	[request performWithBlock: ^ (mtx_response_t response, int statusCode,
	[request performWithBlock: ^ (MTXResponse response, int statusCode,
				       id exception) {
		if (exception != nil) {
			if (_syncExceptionHandler != NULL)
				_syncExceptionHandler(exception);
			return;
		}

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







-
+




-
-
+
+




















-
+




-
+







}

- (void)stopSyncLoop
{
	_syncing = false;
}

- (void)logOutWithBlock: (mtx_client_response_block_t)block
- (void)logOutWithBlock: (MTXClientResponseBlock)block
{
	void *pool = objc_autoreleasePoolPush();
	MTXRequest *request =
	    [self requestWithPath: @"/_matrix/client/r0/logout"];
	request.method = OF_HTTP_REQUEST_METHOD_POST;
	[request performWithBlock: ^ (mtx_response_t response, int statusCode,
	request.method = OFHTTPRequestMethodPost;
	[request performWithBlock: ^ (MTXResponse response, int statusCode,
				       id exception) {
		if (exception != nil) {
			block(exception);
			return;
		}

		if (statusCode != 200) {
			block([MTXLogoutFailedException
			    exceptionWithStatusCode: statusCode
					   response: response
					     client: self]);
			return;
		}

		block(nil);
	}];

	objc_autoreleasePoolPop(pool);
}

- (void)fetchRoomListWithBlock: (mtx_client_room_list_block_t)block
- (void)fetchRoomListWithBlock: (MTXClientRoomListBlock)block
{
	void *pool = objc_autoreleasePoolPush();
	MTXRequest *request =
	    [self requestWithPath: @"/_matrix/client/r0/joined_rooms"];
	[request performWithBlock: ^ (mtx_response_t response, int statusCode,
	[request performWithBlock: ^ (MTXResponse response, int statusCode,
				       id exception) {
		if (exception != nil) {
			block(nil, exception);
			return;
		}

		if (statusCode != 200) {
337
338
339
340
341
342
343
344

345
346
347
348
349
350
351


352
353
354
355
356
357
358
337
338
339
340
341
342
343

344

345
346
347
348


349
350
351
352
353
354
355
356
357







-
+
-




-
-
+
+








		block(response[@"joined_rooms"], nil);
	}];

	objc_autoreleasePoolPop(pool);
}

- (void)joinRoom: (OFString *)room
- (void)joinRoom: (OFString *)room block: (MTXClientRoomJoinBlock)block
	   block: (mtx_client_room_join_block_t)block
{
	void *pool = objc_autoreleasePoolPush();
	MTXRequest *request = [self requestWithPath:
	    [OFString stringWithFormat: @"/_matrix/client/r0/join/%@", room]];
	request.method = OF_HTTP_REQUEST_METHOD_POST;
	[request performWithBlock: ^ (mtx_response_t response, int statusCode,
	request.method = OFHTTPRequestMethodPost;
	[request performWithBlock: ^ (MTXResponse response, int statusCode,
				       id exception) {
		if (exception != nil) {
			block(nil, exception);
			return;
		}

		if (statusCode != 200) {
372
373
374
375
376
377
378
379

380
381
382
383
384
385
386


387
388
389
390
391
392
393
371
372
373
374
375
376
377

378

379
380
381
382


383
384
385
386
387
388
389
390
391







-
+
-




-
-
+
+








		block(roomID, nil);
	}];

	objc_autoreleasePoolPop(pool);
}

- (void)leaveRoom: (OFString *)roomID
- (void)leaveRoom: (OFString *)roomID block: (MTXClientResponseBlock)block
	    block: (mtx_client_response_block_t)block
{
	void *pool = objc_autoreleasePoolPush();
	MTXRequest *request = [self requestWithPath: [OFString
	    stringWithFormat: @"/_matrix/client/r0/rooms/%@/leave", roomID]];
	request.method = OF_HTTP_REQUEST_METHOD_POST;
	[request performWithBlock: ^ (mtx_response_t response, int statusCode,
	request.method = OFHTTPRequestMethodPost;
	[request performWithBlock: ^ (MTXResponse response, int statusCode,
				       id exception) {
		if (exception != nil) {
			block(exception);
			return;
		}

		if (statusCode != 200) {
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
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







-
+





-
+




-
+







	}];

	objc_autoreleasePoolPop(pool);
}

- (void)sendMessage: (OFString *)message
	     roomID: (OFString *)roomID
	      block: (mtx_client_response_block_t)block;
	      block: (MTXClientResponseBlock)block;
{
	void *pool = objc_autoreleasePoolPush();
	OFString *path = [OFString stringWithFormat:
	    @"/_matrix/client/r0/rooms/%@/send/m.room.message", roomID];
	MTXRequest *request = [self requestWithPath: path];
	request.method = OF_HTTP_REQUEST_METHOD_POST;
	request.method = OFHTTPRequestMethodPost;
	request.body = @{
		@"msgtype": @"m.text",
		@"body": message
	};
	[request performWithBlock: ^ (mtx_response_t response, int statusCode,
	[request performWithBlock: ^ (MTXResponse response, int statusCode,
				       id exception) {
		if (exception != nil) {
			block(exception);
			return;
		}

		if (statusCode != 200) {
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
487
488
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







-
+
-














-
+
-



- (void)processJoinedRooms: (OFDictionary<OFString *, id> *)rooms
{
	if (rooms == nil)
		return;

	for (OFString *roomID in rooms)
		[_storage addJoinedRoom: roomID
		[_storage addJoinedRoom: roomID forUser: _userID];
				forUser: _userID];
}

- (void)processInvitedRooms: (OFDictionary<OFString *, id> *)rooms
{
	if (rooms == nil)
		return;
}

- (void)processLeftRooms: (OFDictionary<OFString *, id> *)rooms
{
	if (rooms == nil)
		return;

	for (OFString *roomID in rooms)
		[_storage removeJoinedRoom: roomID
		[_storage removeJoinedRoom: roomID forUser: _userID];
				   forUser: _userID];
}
@end

Modified src/MTXRequest.h from [a3c3ccbf3a] to [df12428c28].

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







-
+









-
-
+
+







OF_ASSUME_NONNULL_BEGIN

/**
 * @brief A response to a request.
 *
 * This is a typedef for `OFDictionary<OFString *, id> *`.
 */
typedef OFDictionary<OFString *, id> *mtx_response_t;
typedef OFDictionary<OFString *, id> *MTXResponse;

/**
 * @brief A block called with the response for an MTXRequest.
 *
 * @param response The response to the request, as a dictionary parsed from JSON
 * @param statusCode The HTTP status code returned for the request
 * @param exception The first exception that occurred during the request,
 *		    or `nil` on success
 */
typedef void (^mtx_request_block_t)(mtx_response_t _Nullable response,
    int statusCode, id _Nullable exception);
typedef void (^MTXRequestBlock)(MTXResponse _Nullable response, int statusCode,
    id _Nullable exception);

/**
 * @brief An internal class for performing a request on the Matrix server.
 */
@interface MTXRequest: OFObject <OFHTTPClientDelegate>
/**
 * @brief The access token to use.
59
60
61
62
63
64
65
66

67
68
69
70
71
72
73
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73







-
+







@property (readonly, nonatomic) OFURL *homeserver;

/**
 * @brief The HTTP request method.
 *
 * Defaults to `OF_HTTP_REQUEST_METHOD_GET`.
 */
@property (nonatomic) of_http_request_method_t method;
@property (nonatomic) OFHTTPRequestMethod method;

/**
 * @brief The path of the request.
 */
@property (copy, nonatomic) OFString *path;

/**
108
109
110
111
112
113
114
115

116
117
118
108
109
110
111
112
113
114

115
116
117
118







-
+




/**
 * @brief Performs the request and calls the specified block once the request
 *	  succeeded or failed.
 *
 * @param block The block to call once the request succeeded or failed
 */
- (void)performWithBlock: (mtx_request_block_t)block;
- (void)performWithBlock: (MTXRequestBlock)block;
@end

OF_ASSUME_NONNULL_END

Modified src/MTXRequest.m from [e2e1bfaf11] to [bc5bbe8ab6].

21
22
23
24
25
26
27
28

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

28
29
30
31
32
33
34
35







-
+







 */

#import "MTXRequest.h"

@implementation MTXRequest
{
	OFData *_body;
	mtx_request_block_t _block;
	MTXRequestBlock _block;
}

+ (instancetype)requestWithPath: (OFString *)path
		    accessToken: (OFString *)accessToken
		     homeserver: (OFURL *)homeserver
{
	return [[[self alloc] initWithPath: path
43
44
45
46
47
48
49
50

51
52
53
54
55
56
57
43
44
45
46
47
48
49

50
51
52
53
54
55
56
57







-
+







{
	self = [super init];

	@try {
		_accessToken = [accessToken copy];
		_homeserver = [homeserver copy];
		_path = [path copy];
		_method = OF_HTTP_REQUEST_METHOD_GET;
		_method = OFHTTPRequestMethodGet;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}
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
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







-
+
-
-
+







-
+
-


-
+







- (void)setBody: (OFDictionary<OFString *, id> *)body
{
	void *pool = objc_autoreleasePoolPush();

	[_body release];

	OFString *JSONString = [body JSONRepresentation];
	_body = [[OFData alloc]
	_body = [[OFData alloc] initWithItems: JSONString.UTF8String
	    initWithItems: JSONString.UTF8String
		    count: JSONString.UTF8StringLength];
					count: JSONString.UTF8StringLength];

	objc_autoreleasePoolPop(pool);
}

- (OFDictionary<OFString *, id> *)body
{
	return [OFString stringWithUTF8String: _body.items
				       length: _body.count]
				       length: _body.count].objectByParsingJSON;
	    .objectByParsingJSON;
}

- (void)performWithBlock: (mtx_request_block_t)block
- (void)performWithBlock: (MTXRequestBlock)block
{
	void *pool = objc_autoreleasePoolPush();

	if (_block != nil)
		/* Not the best exception to indicate it's already in-flight. */
		@throw [OFAlreadyConnectedException exception];

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







-
+










-
+
-


-
+







	  exception: (id)exception
{
	if (response != nil &&
	    [exception isKindOfClass: [OFHTTPRequestFailedException class]])
		exception = nil;

	/* Reset to nil first, so that another one can be performed. */
	mtx_request_block_t block = _block;
	MTXRequestBlock block = _block;
	_block = nil;

	if (exception == nil) {
		@try {
			OFMutableData *responseData = [OFMutableData data];
			while (!response.atEndOfStream) {
				char buffer[512];
				size_t length = [response readIntoBuffer: buffer
								  length: 512];

				[responseData addItems: buffer
				[responseData addItems: buffer count: length];
						 count: length];
			}

			mtx_response_t responseJSON = [OFString
			MTXResponse responseJSON = [OFString
			    stringWithUTF8String: responseData.items
					  length: responseData.count]
			    .objectByParsingJSON;

			block(responseJSON, response.statusCode, nil);
		} @catch (id e) {
			block(nil, response.statusCode, e);

Modified src/MTXSQLite3Storage.m from [28eaed72b7] to [271ad345ab].

102
103
104
105
106
107
108
109

110
111
112
113
114

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

109
110
111
112
113

114

115
116
117
118
119
120
121







-
+




-
+
-







	    @"CREATE TABLE IF NOT EXISTS joined_rooms (\n"
	    @"    user_id TEXT,\n"
	    @"    room_id TEXT,\n"
	    @"    PRIMARY KEY (user_id, room_id)\n"
	    @");"];
}

- (void)transactionWithBlock: (mtx_storage_transaction_block_t)block
- (void)transactionWithBlock: (MTXStorageTransactionBlock)block
{
	[_conn transactionWithBlock: block];
}

- (void)setNextBatch: (OFString *)nextBatch
- (void)setNextBatch: (OFString *)nextBatch forDeviceID: (OFString *)deviceID
	 forDeviceID: (OFString *)deviceID
{
	void *pool = objc_autoreleasePoolPush();

	[_nextBatchSetStatement reset];
	[_nextBatchSetStatement bindWithDictionary: @{
		@"$device_id": deviceID,
		@"$next_batch": nextBatch
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
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







-
+
-













-
+
-



















-
+
-
-










	    [_nextBatchGetStatement.rowDictionary[@"next_batch"] retain];

	objc_autoreleasePoolPop(pool);

	return [nextBatch autorelease];
}

- (void)addJoinedRoom: (OFString *)roomID
- (void)addJoinedRoom: (OFString *)roomID forUser: (OFString *)userID
	      forUser: (OFString *)userID
{
	void *pool = objc_autoreleasePoolPush();

	[_joinedRoomsAddStatement reset];
	[_joinedRoomsAddStatement bindWithDictionary: @{
		@"$room_id": roomID,
		@"$user_id": userID
	}];
	[_joinedRoomsAddStatement step];

	objc_autoreleasePoolPop(pool);
}

- (void)removeJoinedRoom: (OFString *)roomID
- (void)removeJoinedRoom: (OFString *)roomID forUser: (OFString *)userID
		 forUser: (OFString *)userID
{
	void *pool = objc_autoreleasePoolPush();

	[_joinedRoomsRemoveStatement reset];
	[_joinedRoomsRemoveStatement bindWithDictionary: @{
		@"$room_id": roomID,
		@"$user_id": userID
	}];
	[_joinedRoomsRemoveStatement step];

	objc_autoreleasePoolPop(pool);
}

- (OFArray<OFString *> *)joinedRoomsForUser: (OFString *)userID
{
	OFMutableArray *joinedRooms = [OFMutableArray array];
	void *pool = objc_autoreleasePoolPush();

	[_joinedRoomsGetStatement reset];
	[_joinedRoomsGetStatement bindWithDictionary: @{
	[_joinedRoomsGetStatement bindWithDictionary: @{ @"$user_id": userID }];
		@"$user_id": userID
	}];

	while ([_joinedRoomsGetStatement step])
		[joinedRooms addObject:
		    _joinedRoomsGetStatement.rowDictionary[@"room_id"]];

	objc_autoreleasePoolPop(pool);

	return [joinedRooms autorelease];
}
@end

Modified src/MTXStorage.h from [aaa9d2df3c] to [c5b7a2e904].

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







-
+








-
+







-
+
-

















-
+
-








-
+
-












/**
 * @brief A block which will be treated as a single transaction for the storage.
 *
 * @return Whether the transaction should be committed (`true`) or rolled back
 *	   (`false`).
 */
typedef bool (^mtx_storage_transaction_block_t)(void);
typedef bool (^MTXStorageTransactionBlock)(void);

/**
 * @brief A protocol for a storage to be used by @ref MTXClient.
 */
@protocol MTXStorage <OFObject>
/**
 * @brief Performs all operations inside the block as a transaction.
 */
- (void)transactionWithBlock: (mtx_storage_transaction_block_t)block;
- (void)transactionWithBlock: (MTXStorageTransactionBlock)block;

/**
 * @brief Stores the next batch for the specified device.
 *
 * @param nextBatch The next batch for the device
 * @param deviceID The device for which to store the next batch
 */
- (void)setNextBatch: (OFString *)nextBatch
- (void)setNextBatch: (OFString *)nextBatch forDeviceID: (OFString *)deviceID;
	 forDeviceID: (OFString *)deviceID;

/**
 * @brief Returns the next batch for the specified device.
 *
 * @param deviceID The device ID for which to return the next batch
 * @return The next batch for the specified device, or `nil` if none is
 *	   available.
 */
- (nullable OFString *)nextBatchForDeviceID: (OFString *)deviceID;

/**
 * @brief Adds the specified room ID to the list of joined rooms for the
 *	  specified user ID.
 *
 * @param roomID The room ID to add to the list of joined rooms
 * @param userID The user ID for which to add the room
 */
- (void)addJoinedRoom: (OFString *)roomID
- (void)addJoinedRoom: (OFString *)roomID forUser: (OFString *)userID;
	      forUser: (OFString *)userID;

/**
 * @brief Removes the specified room ID to the list of joined rooms for the
 *	  specified user ID.
 *
 * @param roomID The room ID to add to the list of joined rooms
 * @param userID The user ID for which to add the room
 */
- (void)removeJoinedRoom: (OFString *)roomID
- (void)removeJoinedRoom: (OFString *)roomID forUser: (OFString *)userID;
		 forUser: (OFString *)userID;

/**
 * @brief Returns the joined room IDs for the specified user ID.
 *
 * @param userID The user ID for which to return the joined rooms
 * @return The joined room IDs for the specified user ID
 */
- (OFArray<OFString *> *)joinedRoomsForUser: (OFString *)userID;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/MTXClientException.h from [f343668f2c] to [9ee1fc88b6].

26
27
28
29
30
31
32
33

34
35
36
37

38
39
40

41
42
43
44
45
26
27
28
29
30
31
32

33
34
35
36

37
38
39

40
41
42
43
44
45







-
+



-
+


-
+






OF_ASSUME_NONNULL_BEGIN

@class MTXClient;

@interface MTXClientException: OFException
@property (readonly, nonatomic) int statusCode;
@property (readonly, nonatomic) mtx_response_t response;
@property (readonly, nonatomic) MTXResponse response;
@property (readonly, nonatomic) MTXClient *client;

+ (instancetype)exceptionWithStatusCode: (int)statusCode
			       response: (mtx_response_t)response
			       response: (MTXResponse)response
				 client: (MTXClient *)client;
- (instancetype)initWithStatusCode: (int)statusCode
			  response: (mtx_response_t)respons
			  response: (MTXResponse)respons
			    client: (MTXClient *)client
    OF_DESIGNATED_INITIALIZER;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/MTXClientException.m from [2fdf4e0536] to [1e9caf8ec1].

22
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
22
23
24
25
26
27
28

29
30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45







-
+








-
+








#import "MTXLogoutFailedException.h"

#import "MTXClient.h"

@implementation MTXClientException
+ (instancetype)exceptionWithStatusCode: (int)statusCode
			       response: (mtx_response_t)response
			       response: (MTXResponse)response
				 client: (MTXClient *)client
{
	return [[[self alloc] initWithStatusCode: statusCode
					response: response
					  client: client] autorelease];
}

- (instancetype)initWithStatusCode: (int)statusCode
			  response: (mtx_response_t)response
			  response: (MTXResponse)response
			    client: (MTXClient *)client
{
	self = [super init];

	@try {
		_statusCode = statusCode;
		_response = [response copy];

Modified src/exceptions/MTXJoinRoomFailedException.h from [c24a689f7d] to [0427dc1ac2].

26
27
28
29
30
31
32
33

34
35
36
37

38
39
40

41
42
43
44

45
46
47
48
26
27
28
29
30
31
32

33
34
35
36

37
38
39

40
41
42
43

44
45
46
47
48







-
+



-
+


-
+



-
+





OF_ASSUME_NONNULL_BEGIN

@interface MTXJoinRoomFailedException: MTXClientException
@property (readonly, nonatomic) OFString *room;

+ (instancetype)exceptionWithStatusCode: (int)statusCode
			       response: (mtx_response_t)response
			       response: (MTXResponse)response
				 client: (MTXClient *)client OF_UNAVAILABLE;
+ (instancetype)exceptionWithRoom: (OFString *)room
		       statusCode: (int)statusCode
			 response: (mtx_response_t)response
			 response: (MTXResponse)response
			   client: (MTXClient *)client;
- (instancetype)initWithStatusCode: (int)statusCode
			  response: (mtx_response_t)response
			  response: (MTXResponse)response
			    client: (MTXClient *)client OF_UNAVAILABLE;
- (instancetype)initWithRoom: (OFString *)room
		  statusCode: (int)statusCode
		    response: (mtx_response_t)response
		    response: (MTXResponse)response
		      client: (MTXClient *)client;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/MTXJoinRoomFailedException.m from [6d2b4b0f4f] to [03f637a24d].

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







-
+










-
+







#import "MTXJoinRoomFailedException.h"

#import "MTXClient.h"

@implementation MTXJoinRoomFailedException
+ (instancetype)exceptionWithRoom: (OFString *)room
		       statusCode: (int)statusCode
			 response: (mtx_response_t)response
			 response: (MTXResponse)response
			   client: (MTXClient *)client
{
	return [[[self alloc] initWithRoom: room
				statusCode: statusCode
				  response: response
				    client: client] autorelease];
}

- (instancetype)initWithRoom: (OFString *)room
		  statusCode: (int)statusCode
		    response: (mtx_response_t)response
		    response: (MTXResponse)response
		      client: (MTXClient *)client
{
	self = [super initWithStatusCode: statusCode
				response: response
				  client: client];

	@try {

Modified src/exceptions/MTXLeaveRoomFailedException.h from [0d2a38a4ba] to [e5d31b0687].

26
27
28
29
30
31
32
33

34
35
36
37

38
39
40

41
42
43
44

45
46
47
48
26
27
28
29
30
31
32

33
34
35
36

37
38
39

40
41
42
43

44
45
46
47
48







-
+



-
+


-
+



-
+





OF_ASSUME_NONNULL_BEGIN

@interface MTXLeaveRoomFailedException: MTXClientException
@property (readonly, nonatomic) OFString *roomID;

+ (instancetype)exceptionWithStatusCode: (int)statusCode
			       response: (mtx_response_t)response
			       response: (MTXResponse)response
				 client: (MTXClient *)client OF_UNAVAILABLE;
+ (instancetype)exceptionWithRoomID: (OFString *)roomID
			 statusCode: (int)statusCode
			   response: (mtx_response_t)response
			   response: (MTXResponse)response
			     client: (MTXClient *)client;
- (instancetype)initWithStatusCode: (int)statusCode
			  response: (mtx_response_t)response
			  response: (MTXResponse)response
			    client: (MTXClient *)client OF_UNAVAILABLE;
- (instancetype)initWithRoomID: (OFString *)roomID
		    statusCode: (int)statusCode
		      response: (mtx_response_t)response
		      response: (MTXResponse)response
			client: (MTXClient *)client;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/MTXLeaveRoomFailedException.m from [1ab07a8b06] to [9c73667da3].

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







-
+










-
+







#import "MTXLeaveRoomFailedException.h"

#import "MTXClient.h"

@implementation MTXLeaveRoomFailedException
+ (instancetype)exceptionWithRoomID: (OFString *)roomID
			 statusCode: (int)statusCode
			   response: (mtx_response_t)response
			   response: (MTXResponse)response
			     client: (MTXClient *)client
{
	return [[[self alloc] initWithRoomID: roomID
				  statusCode: statusCode
				    response: response
				      client: client] autorelease];
}

- (instancetype)initWithRoomID: (OFString *)roomID
		    statusCode: (int)statusCode
		      response: (mtx_response_t)response
		      response: (MTXResponse)response
			client: (MTXClient *)client
{
	self = [super initWithStatusCode: statusCode
				response: response
				  client: client];

	@try {

Modified src/exceptions/MTXLoginFailedException.h from [652862dea6] to [292e0da216].

26
27
28
29
30
31
32
33

34
35
36
37
38

39
40
41
42

43
44
45
26
27
28
29
30
31
32

33
34
35
36
37

38
39
40
41

42
43
44
45







-
+




-
+



-
+




OF_ASSUME_NONNULL_BEGIN

@interface MTXLoginFailedException: OFException
@property (readonly, nonatomic) OFString *user;
@property (readonly, nonatomic) OFURL *homeserver;
@property (readonly, nonatomic) int statusCode;
@property (readonly, nonatomic) mtx_response_t response;
@property (readonly, nonatomic) MTXResponse response;

+ (instancetype)exceptionWithUser: (OFString *)user
		       homeserver: (OFURL *)homeserver
		       statusCode: (int)statusCode
			 response: (mtx_response_t)response;
			 response: (MTXResponse)response;
- (instancetype)initWithUser: (OFString *)user
		  homeserver: (OFURL *)homeserver
		  statusCode: (int)statusCode
		    response: (mtx_response_t)response;
		    response: (MTXResponse)response;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/MTXLoginFailedException.m from [9df3447195] to [66dad95c01].

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







-
+










-
+








#import "MTXLoginFailedException.h"

@implementation MTXLoginFailedException
+ (instancetype)exceptionWithUser: (OFString *)user
		       homeserver: (OFURL *)homeserver
		       statusCode: (int)statusCode
			 response: (mtx_response_t)response
			 response: (MTXResponse)response
{
	return [[[self alloc] initWithUser: user
				homeserver: homeserver
				statusCode: statusCode
				  response: response] autorelease];
}

- (instancetype)initWithUser: (OFString *)user
		  homeserver: (OFURL *)homeserver
		  statusCode: (int)statusCode
		    response: (mtx_response_t)response
		    response: (MTXResponse)response
{
	self = [super init];

	@try {
		_user = [user copy];
		_homeserver = [homeserver copy];
		_statusCode = statusCode;

Modified src/exceptions/MTXSendMessageFailedException.h from [d7a7a80695] to [3cec6313dc].

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







-
+




-
+


-
+




-
+




OF_ASSUME_NONNULL_BEGIN

@interface MTXSendMessageFailedException: MTXClientException
@property (readonly, nonatomic) OFString *message;
@property (readonly, nonatomic) OFString *roomID;

+ (instancetype)exceptionWithStatusCode: (int)statusCode
			       response: (mtx_response_t)response
			       response: (MTXResponse)response
				 client: (MTXClient *)client OF_UNAVAILABLE;
+ (instancetype)exceptionWithMessage: (OFString *)message
			      roomID: (OFString *)roomID
			  statusCode: (int)statusCode
			    response: (mtx_response_t)response
			    response: (MTXResponse)response
			      client: (MTXClient *)client;
- (instancetype)initWithStatusCode: (int)statusCode
			  response: (mtx_response_t)response
			  response: (MTXResponse)response
			    client: (MTXClient *)client OF_UNAVAILABLE;
- (instancetype)initWithMessage: (OFString *)message
			 roomID: (OFString *)roomID
		     statusCode: (int)statusCode
		       response: (mtx_response_t)response
		       response: (MTXResponse)response
			 client: (MTXClient *)client OF_DESIGNATED_INITIALIZER;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/MTXSendMessageFailedException.m from [4d0e5a29bd] to [7a18d8f50a].

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







-
+












-
+








#import "MTXClient.h"

@implementation MTXSendMessageFailedException
+ (instancetype)exceptionWithMessage: (OFString *)message
			      roomID: (OFString *)roomID
			  statusCode: (int)statusCode
			    response: (mtx_response_t)response
			    response: (MTXResponse)response
			      client: (MTXClient *)client
{
	return [[[self alloc] initWithMessage: message
				       roomID: roomID
				   statusCode: statusCode
				     response: response
				       client: client] autorelease];
}

- (instancetype)initWithMessage: (OFString *)message
			 roomID: (OFString *)roomID
		     statusCode: (int)statusCode
		       response: (mtx_response_t)response
		       response: (MTXResponse)response
			 client: (MTXClient *)client
{
	self = [super initWithStatusCode: statusCode
				response: response
				  client: client];

	@try {

Modified tests/tests.m from [bf6cef9450] to [16dc45e5ea].

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







-
-
-
+
+
+












-
+




-
+











-
+



-
+








-
-
+

-
+




-
+











-
+




-
+

-
-
+

-
+
-





-
+
-

-
+



-
+









-
+



-
+






- (void)applicationDidFinishLaunching
{
	__auto_type environment = OFApplication.environment;
	if (environment[@"OBJMATRIX_USER"] == nil ||
	    environment[@"OBJMATRIX_PASS"] == nil ||
	    environment[@"OBJMATRIX_HS"] == nil) {
		[of_stderr writeString: @"Please set OBJMATRIX_USER, "
					@"OBJMATRIX_PASS and OBJMATRIX_HS in "
					@"the environment!\n"];
		[OFStdErr writeString: @"Please set OBJMATRIX_USER, "
				       @"OBJMATRIX_PASS and OBJMATRIX_HS in "
				       @"the environment!\n"];
		[OFApplication terminateWithStatus: 1];
	}

	OFURL *homeserver = [OFURL URLWithString: environment[@"OBJMATRIX_HS"]];
	id <MTXStorage> storage =
	    [MTXSQLite3Storage storageWithPath: @"tests.db"];
	[MTXClient logInWithUser: environment[@"OBJMATRIX_USER"]
			password: environment[@"OBJMATRIX_PASS"]
		      homeserver: homeserver
			 storage: storage
			   block: ^ (MTXClient *client, id exception) {
		if (exception != nil) {
			of_log(@"Error logging in: %@", exception);
			OFLog(@"Error logging in: %@", exception);
			[OFApplication terminateWithStatus: 1];
		}

		_client = [client retain];
		of_log(@"Logged in client: %@", _client);
		OFLog(@"Logged in client: %@", _client);

		[_client startSyncLoop];
		[self fetchRoomList];
	}];
}

- (void)fetchRoomList
{
	[_client fetchRoomListWithBlock: ^ (OFArray<OFString *> *rooms,
					     id exception) {
		if (exception != nil) {
			of_log(@"Failed to fetch room list: %@", exception);
			OFLog(@"Failed to fetch room list: %@", exception);
			[OFApplication terminateWithStatus: 1];
		}

		of_log(@"Fetched room list: %@", rooms);
		OFLog(@"Fetched room list: %@", rooms);

		[self joinRoom];
	}];
}

- (void)joinRoom
{
	OFString *room = @"#test:nil.im";
	[_client joinRoom: room
		    block: ^ (OFString *roomID, id exception) {
	[_client joinRoom: room block: ^ (OFString *roomID, id exception) {
		if (exception != nil) {
			of_log(@"Failed to join room %@: %@", room, exception);
			OFLog(@"Failed to join room %@: %@", room, exception);
			[OFApplication terminateWithStatus: 1];
		}

		_roomID = [roomID copy];
		of_log(@"Joined room %@", _roomID);
		OFLog(@"Joined room %@", _roomID);

		[self sendMessage];
	}];
}

- (void)sendMessage
{
	[_client sendMessage: @"ObjMatrix test successful!"
		      roomID: _roomID
		       block: ^ (id exception) {
		if (exception != nil) {
			of_log(@"Failed to send message to room %@: %@",
			OFLog(@"Failed to send message to room %@: %@",
			    _roomID, exception);
			[OFApplication terminateWithStatus: 1];
		}

		of_log(@"Message sent to %@", _roomID);
		OFLog(@"Message sent to %@", _roomID);

		of_log(
		    @"Waiting 5 seconds before leaving room and logging out");
		OFLog(@"Waiting 5 seconds before leaving room and logging out");

		[self performSelector: @selector(leaveRoom)
		[self performSelector: @selector(leaveRoom) afterDelay: 5];
			   afterDelay: 5];
	}];
}

- (void)leaveRoom
{
	[_client leaveRoom: _roomID
	[_client leaveRoom: _roomID block: ^ (id exception) {
		     block: ^ (id exception) {
		if (exception != nil) {
			of_log(@"Failed to leave room %@: %@", exception);
			OFLog(@"Failed to leave room %@: %@", exception);
			[OFApplication terminateWithStatus: 1];
		}

		of_log(@"Left room %@", _roomID);
		OFLog(@"Left room %@", _roomID);

		[self logOut];
	}];
}

- (void)logOut
{
	[_client logOutWithBlock: ^ (id exception) {
		if (exception != nil) {
			of_log(@"Failed to log out: %@\n", exception);
			OFLog(@"Failed to log out: %@\n", exception);
			[OFApplication terminateWithStatus: 1];
		}

		of_log(@"Logged out client");
		OFLog(@"Logged out client");

		[OFApplication terminate];
	}];
}
@end