Index: src/MTXClient.h ================================================================== --- src/MTXClient.h +++ src/MTXClient.h @@ -59,10 +59,17 @@ * @param exception An exception if joining the room failed */ typedef void (^mtx_client_room_join_block_t)(OFString *_Nullable roomID, id _Nullable exception); +/** + * @brief A block called when a room was left. + * + * @param exception An exception if leaving the room failed + */ +typedef void (^mtx_client_room_leave_block_t)(id _Nullable exception); + /** * @brief A class that represents a client. */ @interface MTXClient: OFObject /** @@ -141,8 +148,17 @@ * @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 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 + block: (mtx_client_room_leave_block_t)block; @end OF_ASSUME_NONNULL_END Index: src/MTXClient.m ================================================================== --- src/MTXClient.m +++ src/MTXClient.m @@ -23,10 +23,11 @@ #import "MTXClient.h" #import "MTXRequest.h" #import "MTXFetchRoomListFailedException.h" #import "MTXJoinRoomFailedException.h" +#import "MTXLeaveRoomFailedException.h" #import "MTXLoginFailedException.h" #import "MTXLogoutFailedException.h" static void validateHomeserver(OFURL *homeserver) @@ -282,9 +283,38 @@ return; } block(roomID, nil); }]; + + objc_autoreleasePoolPop(pool); +} + +- (void)leaveRoom: (OFString *)roomID + block: (mtx_client_room_leave_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, + id exception) { + if (exception != nil) { + block(exception); + return; + } + + if (statusCode != 200) { + block([MTXLeaveRoomFailedException + exceptionWithRoomID: roomID + statusCode: statusCode + response: response + client: self]); + return; + } + + block(nil); + }]; objc_autoreleasePoolPop(pool); } @end Index: src/ObjMatrix.h ================================================================== --- src/ObjMatrix.h +++ src/ObjMatrix.h @@ -22,7 +22,9 @@ #import "MTXClient.h" #import "MTXRequest.h" #import "MTXFetchRoomListFailedException.h" +#import "MTXJoinRoomFailedException.h" +#import "MTXLeaveRoomFailedException.h" #import "MTXLoginFailedException.h" #import "MTXLogoutFailedException.h" ADDED src/exceptions/MTXLeaveRoomFailedException.h Index: src/exceptions/MTXLeaveRoomFailedException.h ================================================================== --- src/exceptions/MTXLeaveRoomFailedException.h +++ src/exceptions/MTXLeaveRoomFailedException.h @@ -0,0 +1,48 @@ +/* + * 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 "MTXClientException.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface MTXLeaveRoomFailedException: MTXClientException +@property (readonly, nonatomic) OFString *roomID; + ++ (instancetype)exceptionWithStatusCode: (int)statusCode + response: (mtx_response_t)response + client: (MTXClient *)client OF_UNAVAILABLE; ++ (instancetype)exceptionWithRoomID: (OFString *)roomID + statusCode: (int)statusCode + response: (mtx_response_t)response + client: (MTXClient *)client; +- (instancetype)initWithStatusCode: (int)statusCode + response: (mtx_response_t)response + client: (MTXClient *)client OF_UNAVAILABLE; +- (instancetype)initWithRoomID: (OFString *)roomID + statusCode: (int)statusCode + response: (mtx_response_t)response + client: (MTXClient *)client; +@end + +OF_ASSUME_NONNULL_END ADDED src/exceptions/MTXLeaveRoomFailedException.m Index: src/exceptions/MTXLeaveRoomFailedException.m ================================================================== --- src/exceptions/MTXLeaveRoomFailedException.m +++ src/exceptions/MTXLeaveRoomFailedException.m @@ -0,0 +1,71 @@ +/* + * 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 "MTXLeaveRoomFailedException.h" + +#import "MTXClient.h" + +@implementation MTXLeaveRoomFailedException ++ (instancetype)exceptionWithRoomID: (OFString *)roomID + statusCode: (int)statusCode + response: (mtx_response_t)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 + client: (MTXClient *)client +{ + self = [super initWithStatusCode: statusCode + response: response + client: client]; + + @try { + _roomID = [roomID copy]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_roomID release]; + + [super dealloc]; +} + +- (OFString *)description +{ + return [OFString stringWithFormat: + @"Failed to leave room %@ for %@: %@", + _roomID, self.client.userID, self.response]; +} +@end Index: src/exceptions/Makefile ================================================================== --- src/exceptions/Makefile +++ src/exceptions/Makefile @@ -4,13 +4,14 @@ STATIC_LIB_NOINST = ${EXCEPTIONS_A} SRCS = MTXClientException.m \ MTXFetchRoomListFailedException.m \ MTXJoinRoomFailedException.m \ + MTXLeaveRoomFailedException.m \ MTXLoginFailedException.m \ MTXLogoutFailedException.m INCLUDES = ${SRCS:.m=.h} include ../../buildsys.mk CPPFLAGS += -I. -I.. Index: tests/tests.m ================================================================== --- tests/tests.m +++ tests/tests.m @@ -78,19 +78,35 @@ }]; } - (void)joinRoom { - [_client joinRoom: @"#test:nil.im" + OFString *room = @"#test:nil.im"; + [_client joinRoom: room block: ^ (OFString *roomID, id exception) { if (exception != nil) { - of_log(@"Failed to join room: %@", exception); + of_log(@"Failed to join room %@: %@", room, exception); [OFApplication terminateWithStatus: 1]; } of_log(@"Joined room %@", roomID); + [self leaveRoom: roomID]; + }]; +} + +- (void)leaveRoom: (OFString *)roomID +{ + [_client leaveRoom: roomID + block: ^ (id exception) { + if (exception != nil) { + of_log(@"Failed to leave room %@: %@", exception); + [OFApplication terminateWithStatus: 1]; + } + + of_log(@"Left room %@", roomID); + [self logOut]; }]; } - (void)logOut