From 77a2d20fc7ae748f7c54b1ab462a1c23eb069ec2 Mon Sep 17 00:00:00 2001 From: Jonathan Schleifer Date: Sat, 3 Oct 2020 16:21:34 +0000 Subject: [PATCH] MTXClient: Add support for logging out FossilOrigin-Name: c29845b7b81229ea7bf062304824a4087c160629216064ae1a647fe8286826bb --- src/MTXClient.h | 16 ++++++ src/MTXClient.m | 40 ++++++++++++- src/MTXRequest.h | 12 +++- src/MTXRequest.m | 10 ++-- src/ObjMatrix.h | 1 + src/exceptions/MTXLoginFailedException.h | 8 ++- src/exceptions/MTXLoginFailedException.m | 11 +++- src/exceptions/MTXLogoutFailedException.h | 44 +++++++++++++++ src/exceptions/MTXLogoutFailedException.m | 68 +++++++++++++++++++++++ src/exceptions/Makefile | 3 +- tests/tests.m | 21 ++++--- 11 files changed, 209 insertions(+), 25 deletions(-) create mode 100644 src/exceptions/MTXLogoutFailedException.h create mode 100644 src/exceptions/MTXLogoutFailedException.m diff --git a/src/MTXClient.h b/src/MTXClient.h index 6a88648..0ecda2b 100644 --- a/src/MTXClient.h +++ b/src/MTXClient.h @@ -35,6 +35,13 @@ OF_ASSUME_NONNULL_BEGIN 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. */ @@ -92,6 +99,15 @@ typedef void (^mtx_client_login_block_t)(MTXClient *_Nullable client, 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 diff --git a/src/MTXClient.m b/src/MTXClient.m index 7ad334a..88278ba 100644 --- a/src/MTXClient.m +++ b/src/MTXClient.m @@ -24,6 +24,7 @@ #import "MTXRequest.h" #import "MTXLoginFailedException.h" +#import "MTXLogoutFailedException.h" static void validateHomeserver(OFURL *homeserver) @@ -76,9 +77,8 @@ validateHomeserver(OFURL *homeserver) @"password": password }; - [request asyncPerformWithBlock: - ^ (OFDictionary *response, int statusCode, - id exception) { + [request asyncPerformWithBlock: ^ (mtx_response_t response, + int statusCode, id exception) { if (exception != nil) { block(nil, exception); return; @@ -170,4 +170,38 @@ validateHomeserver(OFURL *homeserver) @">", 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 diff --git a/src/MTXRequest.h b/src/MTXRequest.h index 02913d4..8e72a4e 100644 --- a/src/MTXRequest.h +++ b/src/MTXRequest.h @@ -24,6 +24,13 @@ OF_ASSUME_NONNULL_BEGIN +/** + * @brief A response to a request. + * + * This is a typedef for `OFDictionary *`. + */ +typedef OFDictionary *mtx_response_t; + /** * @brief A block called with the response for an MTXRequest. * @@ -32,9 +39,8 @@ OF_ASSUME_NONNULL_BEGIN * @param exception The first exception that occurred during the request, * or `nil` on success */ -typedef void (^mtx_request_block_t)( - OFDictionary *_Nullable response, int statusCode, - id _Nullable exception); +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. diff --git a/src/MTXRequest.m b/src/MTXRequest.m index a9063d4..8ed625c 100644 --- a/src/MTXRequest.m +++ b/src/MTXRequest.m @@ -101,8 +101,8 @@ OFMutableDictionary *headers = [OFMutableDictionary dictionary]; headers[@"User-Agent"] = @"ObjMatrix"; if (_accessToken != nil) - headers[@"Authentication"] = [OFString - stringWithFormat: @"Bearer %@", _accessToken]; + headers[@"Authorization"] = + [OFString stringWithFormat: @"Bearer %@", _accessToken]; if (_body != nil) headers[@"Content-Length"] = @(_body.count).stringValue; @@ -139,9 +139,9 @@ count: length]; } - OFDictionary *responseJSON = - [OFString stringWithUTF8String: responseData.items - length: responseData.count] + mtx_response_t responseJSON = [OFString + stringWithUTF8String: responseData.items + length: responseData.count] .objectByParsingJSON; block(responseJSON, response.statusCode, nil); diff --git a/src/ObjMatrix.h b/src/ObjMatrix.h index 5af5dfb..a7cdadd 100644 --- a/src/ObjMatrix.h +++ b/src/ObjMatrix.h @@ -24,3 +24,4 @@ #import "MTXRequest.h" #import "MTXLoginFailedException.h" +#import "MTXLogoutFailedException.h" diff --git a/src/exceptions/MTXLoginFailedException.h b/src/exceptions/MTXLoginFailedException.h index 1fadfbe..9d97c66 100644 --- a/src/exceptions/MTXLoginFailedException.h +++ b/src/exceptions/MTXLoginFailedException.h @@ -22,22 +22,24 @@ #import +#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) OFDictionary *response; +@property (readonly, nonatomic) mtx_response_t response; + (instancetype)exceptionWithUser: (OFString *)user homeserver: (OFURL *)homeserver statusCode: (int)statusCode - response: (OFDictionary *)response; + response: (mtx_response_t)response; - (instancetype)initWithUser: (OFString *)user homeserver: (OFURL *)homeserver statusCode: (int)statusCode - response: (OFDictionary *)response; + response: (mtx_response_t)response; @end OF_ASSUME_NONNULL_END diff --git a/src/exceptions/MTXLoginFailedException.m b/src/exceptions/MTXLoginFailedException.m index 2c7e2d7..aa1257f 100644 --- a/src/exceptions/MTXLoginFailedException.m +++ b/src/exceptions/MTXLoginFailedException.m @@ -26,7 +26,7 @@ + (instancetype)exceptionWithUser: (OFString *)user homeserver: (OFURL *)homeserver statusCode: (int)statusCode - response: (OFDictionary *)response + response: (mtx_response_t)response { return [[[self alloc] initWithUser: user homeserver: homeserver @@ -37,7 +37,7 @@ - (instancetype)initWithUser: (OFString *)user homeserver: (OFURL *)homeserver statusCode: (int)statusCode - response: (OFDictionary *)response + response: (mtx_response_t)response { self = [super init]; @@ -62,4 +62,11 @@ [super dealloc]; } + +- (OFString *)description +{ + return [OFString stringWithFormat: + @"Failed to log in user %@ on %@: %@", + _user, _homeserver, _response]; +} @end diff --git a/src/exceptions/MTXLogoutFailedException.h b/src/exceptions/MTXLogoutFailedException.h new file mode 100644 index 0000000..4bd586c --- /dev/null +++ b/src/exceptions/MTXLogoutFailedException.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020, Jonathan Schleifer + * + * 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 + +#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 diff --git a/src/exceptions/MTXLogoutFailedException.m b/src/exceptions/MTXLogoutFailedException.m new file mode 100644 index 0000000..e6be63c --- /dev/null +++ b/src/exceptions/MTXLogoutFailedException.m @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020, Jonathan Schleifer + * + * 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 diff --git a/src/exceptions/Makefile b/src/exceptions/Makefile index 17ea308..efb0d0b 100644 --- a/src/exceptions/Makefile +++ b/src/exceptions/Makefile @@ -3,7 +3,8 @@ include ../../extra.mk STATIC_PIC_LIB_NOINST = ${EXCEPTIONS_LIB_A} STATIC_LIB_NOINST = ${EXCEPTIONS_A} -SRCS = MTXLoginFailedException.m +SRCS = MTXLoginFailedException.m \ + MTXLogoutFailedException.m INCLUDES = ${SRCS:.m=.h} include ../../buildsys.mk diff --git a/tests/tests.m b/tests/tests.m index a9d8110..abfd53f 100644 --- a/tests/tests.m +++ b/tests/tests.m @@ -48,17 +48,22 @@ OF_APPLICATION_DELEGATE(Tests) 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]]; + of_log(@"Error logging in: %@", exception); [OFApplication terminateWithStatus: 1]; } - [of_stdout writeFormat: @"Logged in client: %@\n", client]; - [OFApplication terminate]; + 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