ObjMatrix  Check-in [c29845b7b8]

Overview
Comment:MTXClient: Add support for logging out
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: c29845b7b81229ea7bf062304824a4087c160629216064ae1a647fe8286826bb
User & Date: js on 2020-10-03 16:21:34
Other Links: manifest | tags
Context
2020-10-03
17:08
Add support for fetching room list check-in: 092c122c69 user: js tags: trunk
16:21
MTXClient: Add support for logging out check-in: c29845b7b8 user: js tags: trunk
15:32
tests: Print better error messages check-in: 86b43015f0 user: js tags: trunk
Changes

Modified src/MTXClient.h from [f435b302a4] to [04928e6321].

31
32
33
34
35
36
37







38
39
40
41
42
43
44
 *
 * @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,
    id _Nullable exception);








/**
 * @brief A class that represents a client.
 */
@interface MTXClient: OFObject
/**
 * @brief The user ID used by the client.
 */







>
>
>
>
>
>
>







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
 *
 * @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,
    id _Nullable exception);

/**
 * @brief A block called when the device was logged out.
 *
 * @param exception `nil` on success, otherwise an exception
 */
typedef void (^mtx_client_logout_block_t)(id _Nullable exception);

/**
 * @brief A class that represents a client.
 */
@interface MTXClient: OFObject
/**
 * @brief The user ID used by the client.
 */
88
89
90
91
92
93
94









95
96
97
 * @param homeserver The URL of the homeserver
 * @return An initialized MTXClient
 */
- (instancetype)initWithUserID: (OFString *)userID
		      deviceID: (OFString *)deviceID
		   accessToken: (OFString *)accessToken
		    homeserver: (OFURL *)homeserver OF_DESIGNATED_INITIALIZER;









@end

OF_ASSUME_NONNULL_END







>
>
>
>
>
>
>
>
>



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
 * @param homeserver The URL of the homeserver
 * @return An initialized MTXClient
 */
- (instancetype)initWithUserID: (OFString *)userID
		      deviceID: (OFString *)deviceID
		   accessToken: (OFString *)accessToken
		    homeserver: (OFURL *)homeserver OF_DESIGNATED_INITIALIZER;

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

OF_ASSUME_NONNULL_END

Modified src/MTXClient.m from [bbc38be695] to [745a831094].

20
21
22
23
24
25
26

27
28
29
30
31
32
33
 * POSSIBILITY OF SUCH DAMAGE.
 */

#import "MTXClient.h"
#import "MTXRequest.h"

#import "MTXLoginFailedException.h"


static void
validateHomeserver(OFURL *homeserver)
{
	if (![homeserver.scheme isEqual: @"http"] &&
	    ![homeserver.scheme isEqual: @"https"])
		@throw [OFUnsupportedProtocolException







>







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 * POSSIBILITY OF SUCH DAMAGE.
 */

#import "MTXClient.h"
#import "MTXRequest.h"

#import "MTXLoginFailedException.h"
#import "MTXLogoutFailedException.h"

static void
validateHomeserver(OFURL *homeserver)
{
	if (![homeserver.scheme isEqual: @"http"] &&
	    ![homeserver.scheme isEqual: @"https"])
		@throw [OFUnsupportedProtocolException
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
		@"identifier": @{
			@"type": @"m.id.user",
			@"user": user
		},
		@"password": password
	};

	[request asyncPerformWithBlock:
	    ^ (OFDictionary<OFString *, id> *response, int statusCode,
	    id exception) {
		if (exception != nil) {
			block(nil, exception);
			return;
		}

		if (statusCode != 200) {
			id exception = [MTXLoginFailedException







|
<
|







73
74
75
76
77
78
79
80

81
82
83
84
85
86
87
88
		@"identifier": @{
			@"type": @"m.id.user",
			@"user": user
		},
		@"password": password
	};

	[request asyncPerformWithBlock: ^ (mtx_response_t response,

					    int statusCode, id exception) {
		if (exception != nil) {
			block(nil, exception);
			return;
		}

		if (statusCode != 200) {
			id exception = [MTXLoginFailedException
166
167
168
169
170
171
172


































173
	    @"\tUser ID = %@\n"
	    @"\tDevice ID = %@\n"
	    @"\tAccess token = %@\n"
	    @"\tHomeserver = %@\n"
	    @">",
	    self.class, _userID, _deviceID, _accessToken, _homeserver];
}


































@end







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

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
	    @"\tUser ID = %@\n"
	    @"\tDevice ID = %@\n"
	    @"\tAccess token = %@\n"
	    @"\tHomeserver = %@\n"
	    @">",
	    self.class, _userID, _deviceID, _accessToken, _homeserver];
}

- (MTXRequest *)requestWithPath: (OFString *)path
{
	return [MTXRequest requestWithPath: path
			       accessToken: _accessToken
				homeserver: _homeserver];
}

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

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

		block(nil);
	}];

	objc_autoreleasePoolPop(pool);
}
@end

Modified src/MTXRequest.h from [dd16951835] to [2eb6986a25].

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
 * POSSIBILITY OF SUCH DAMAGE.
 */

#import <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN








/**
 * @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)(
    OFDictionary<OFString *, id> *_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.







>
>
>
>
>
>
>








|
<
|







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
 * POSSIBILITY OF SUCH DAMAGE.
 */

#import <ObjFW/ObjFW.h>

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;

/**
 * @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);

/**
 * @brief An internal class for performing a request on the Matrix server.
 */
@interface MTXRequest: OFObject <OFHTTPClientDelegate>
/**
 * @brief The access token to use.

Modified src/MTXRequest.m from [eb63bbbdde] to [8a0c920770].

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

	OFMutableURL *requestURL = [[_homeserver mutableCopy] autorelease];
	requestURL.path = _path;

	OFMutableDictionary *headers = [OFMutableDictionary dictionary];
	headers[@"User-Agent"] = @"ObjMatrix";
	if (_accessToken != nil)
		headers[@"Authentication"] = [OFString
		    stringWithFormat: @"Bearer %@", _accessToken];
	if (_body != nil)
		headers[@"Content-Length"] = @(_body.count).stringValue;

	OFHTTPRequest *request = [OFHTTPRequest requestWithURL: requestURL];
	request.method = _method;
	request.headers = headers;








|
|







97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

	OFMutableURL *requestURL = [[_homeserver mutableCopy] autorelease];
	requestURL.path = _path;

	OFMutableDictionary *headers = [OFMutableDictionary dictionary];
	headers[@"User-Agent"] = @"ObjMatrix";
	if (_accessToken != nil)
		headers[@"Authorization"] =
		    [OFString stringWithFormat: @"Bearer %@", _accessToken];
	if (_body != nil)
		headers[@"Content-Length"] = @(_body.count).stringValue;

	OFHTTPRequest *request = [OFHTTPRequest requestWithURL: requestURL];
	request.method = _method;
	request.headers = headers;

135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
			size_t length = [response readIntoBuffer: buffer
							  length: 512];

			[responseData addItems: buffer
					 count: length];
		}

		OFDictionary<OFString *, id> *responseJSON =
		    [OFString stringWithUTF8String: responseData.items
					    length: responseData.count]
		    .objectByParsingJSON;

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








|
|
|







135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
			size_t length = [response readIntoBuffer: buffer
							  length: 512];

			[responseData addItems: buffer
					 count: length];
		}

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

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

Modified src/ObjMatrix.h from [b15ca8b4fe] to [85f68e7b26].

20
21
22
23
24
25
26

 * POSSIBILITY OF SUCH DAMAGE.
 */

#import "MTXClient.h"
#import "MTXRequest.h"

#import "MTXLoginFailedException.h"








>
20
21
22
23
24
25
26
27
 * POSSIBILITY OF SUCH DAMAGE.
 */

#import "MTXClient.h"
#import "MTXRequest.h"

#import "MTXLoginFailedException.h"
#import "MTXLogoutFailedException.h"

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

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

@interface MTXLoginFailedException: OFException
@property (readonly, nonatomic) OFString *user;
@property (readonly, nonatomic) OFURL *homeserver;
@property (readonly, nonatomic) int statusCode;
@property (readonly, nonatomic) OFDictionary<OFString *, id> *response;

+ (instancetype)exceptionWithUser: (OFString *)user
		       homeserver: (OFURL *)homeserver
		       statusCode: (int)statusCode
			 response: (OFDictionary<OFString *, id> *)response;
- (instancetype)initWithUser: (OFString *)user
		  homeserver: (OFURL *)homeserver
		  statusCode: (int)statusCode
		    response: (OFDictionary<OFString *, id> *)response;
@end

OF_ASSUME_NONNULL_END







>
>







|




|



|



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

#import "MTXRequest.h"

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;

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

OF_ASSUME_NONNULL_END

Modified src/exceptions/MTXLoginFailedException.m from [6ce58b3536] to [900a59b5fd].

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: (OFDictionary<OFString *, id> *)response
{
	return [[[self alloc] initWithUser: user
				homeserver: homeserver
				statusCode: statusCode
				  response: response] autorelease];
}

- (instancetype)initWithUser: (OFString *)user
		  homeserver: (OFURL *)homeserver
		  statusCode: (int)statusCode
		    response: (OFDictionary<OFString *, id> *)response
{
	self = [super init];

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







|










|







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
{
	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
{
	self = [super init];

	@try {
		_user = [user copy];
		_homeserver = [homeserver copy];
		_statusCode = statusCode;
58
59
60
61
62
63
64







65
{
	[_user release];
	[_homeserver release];
	[_response release];

	[super dealloc];
}







@end







>
>
>
>
>
>
>

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
{
	[_user release];
	[_homeserver release];
	[_response release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to log in user %@ on %@: %@",
	    _user, _homeserver, _response];
}
@end

Added src/exceptions/MTXLogoutFailedException.h version [c3b16f062e].

























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/*
 * Copyright (c) 2020, Jonathan Schleifer <js@nil.im>
 *
 * https://fossil.nil.im/objmatrix
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice is present in all copies.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#import <ObjFW/ObjFW.h>

#import "MTXRequest.h"

OF_ASSUME_NONNULL_BEGIN

@class MTXClient;

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

+ (instancetype)exceptionWithClient: (MTXClient *)client
			 statusCode: (int)statusCode
			   response: (mtx_response_t)response;
- (instancetype)initWithClient: (OFString *)user
		    statusCode: (int)statusCode
		      response: (mtx_response_t)response;
@end

OF_ASSUME_NONNULL_END

Added src/exceptions/MTXLogoutFailedException.m version [dd5ff28e63].









































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/*
 * Copyright (c) 2020, Jonathan Schleifer <js@nil.im>
 *
 * https://fossil.nil.im/objmatrix
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice is present in all copies.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#import "MTXLogoutFailedException.h"

#import "MTXClient.h"

@implementation MTXLogoutFailedException
+ (instancetype)exceptionWithClient: (MTXClient *)client
			 statusCode: (int)statusCode
			   response: (mtx_response_t)response
{
	return [[[self alloc] initWithClient: client
				  statusCode: statusCode
				    response: response] autorelease];
}

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

	@try {
		_client = [client retain];
		_statusCode = statusCode;
		_response = [response copy];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_client release];
	[_response release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat:
	    @"Failed to log out user %@: %@", _client.userID, _response];
}
@end

Modified src/exceptions/Makefile from [c62821a6f5] to [8e5700e68b].

1
2
3
4
5
6

7
8
9
10
11
12
include ../../extra.mk

STATIC_PIC_LIB_NOINST = ${EXCEPTIONS_LIB_A}
STATIC_LIB_NOINST = ${EXCEPTIONS_A}

SRCS = MTXLoginFailedException.m

INCLUDES = ${SRCS:.m=.h}

include ../../buildsys.mk

CPPFLAGS += -I. -I..






|
>






1
2
3
4
5
6
7
8
9
10
11
12
13
include ../../extra.mk

STATIC_PIC_LIB_NOINST = ${EXCEPTIONS_LIB_A}
STATIC_LIB_NOINST = ${EXCEPTIONS_A}

SRCS = MTXLoginFailedException.m	\
       MTXLogoutFailedException.m
INCLUDES = ${SRCS:.m=.h}

include ../../buildsys.mk

CPPFLAGS += -I. -I..

Modified tests/tests.m from [30cdad59c3] to [c6d3f94e02].

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60









61

62
63
64

	OFURL *homeserver = [OFURL URLWithString: environment[@"OBJMATRIX_HS"]];
	[MTXClient logInWithUser: environment[@"OBJMATRIX_USER"]
			password: environment[@"OBJMATRIX_PASS"]
		      homeserver: homeserver
			   block: ^ (MTXClient *client, id exception) {
		if (exception != nil) {
			[of_stdout writeFormat: @"Error logging in: %@\n",
						exception];
			if ([exception isKindOfClass:
			    MTXLoginFailedException.class])
				[of_stdout writeFormat: @"Response: %@\n",
							[exception response]];
			[OFApplication terminateWithStatus: 1];
		}

		[of_stdout writeFormat: @"Logged in client: %@\n", client];









		[OFApplication terminate];

	}];
}
@end







|
<
<
<
<
<



|
>
>
>
>
>
>
>
>
>
|
>



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

	OFURL *homeserver = [OFURL URLWithString: environment[@"OBJMATRIX_HS"]];
	[MTXClient logInWithUser: environment[@"OBJMATRIX_USER"]
			password: environment[@"OBJMATRIX_PASS"]
		      homeserver: homeserver
			   block: ^ (MTXClient *client, id exception) {
		if (exception != nil) {
			of_log(@"Error logging in: %@", exception);





			[OFApplication terminateWithStatus: 1];
		}

		of_log(@"Logged in client: %@", client);

		[client asyncLogOutWithBlock: ^ (id exception) {
			if (exception != nil) {
				of_log(@"Failed to log out: %@\n", exception);
				[OFApplication terminateWithStatus: 1];
			}

			of_log(@"Logged out client");

			[OFApplication terminate];
		}];
	}];
}
@end