Index: src/MTXClient.h ================================================================== --- src/MTXClient.h +++ src/MTXClient.h @@ -49,10 +49,20 @@ * @param exception An exception if fetching the room list failed */ typedef void (^mtx_client_room_list_block_t)( OFArray *_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, + id _Nullable exception); + /** * @brief A class that represents a client. */ @interface MTXClient: OFObject /** @@ -122,8 +132,17 @@ * @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; + +/** + * @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 + block: (mtx_client_room_join_block_t)block; @end OF_ASSUME_NONNULL_END Index: src/MTXClient.m ================================================================== --- src/MTXClient.m +++ src/MTXClient.m @@ -22,10 +22,11 @@ #import "MTXClient.h" #import "MTXRequest.h" #import "MTXFetchRoomListFailedException.h" +#import "MTXJoinRoomFailedException.h" #import "MTXLoginFailedException.h" #import "MTXLogoutFailedException.h" static void validateHomeserver(OFURL *homeserver) @@ -199,13 +200,13 @@ return; } if (statusCode != 200) { block([MTXLogoutFailedException - exceptionWithClient: self - statusCode: statusCode - response: response]); + exceptionWithStatusCode: statusCode + response: response + client: self]); return; } block(nil); }]; @@ -225,13 +226,13 @@ return; } if (statusCode != 200) { block(nil, [MTXFetchRoomListFailedException - exceptionWithClient: self - statusCode: statusCode - response: response]); + exceptionWithStatusCode: statusCode + response: response + client: self]); return; } OFArray *joinedRooms = response[@"joined_rooms"]; if (![joinedRooms isKindOfClass: OFArray.class]) { @@ -246,9 +247,44 @@ } } block(response[@"joined_rooms"], nil); }]; + + objc_autoreleasePoolPop(pool); +} + +- (void)joinRoom: (OFString *)room + 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, + id exception) { + if (exception != nil) { + block(nil, exception); + return; + } + + if (statusCode != 200) { + block(nil, [MTXJoinRoomFailedException + exceptionWithRoom: room + statusCode: statusCode + response: response + client: self]); + return; + } + + OFString *roomID = response[@"room_id"]; + if (![roomID isKindOfClass: OFString.class]) { + block(nil, [OFInvalidServerReplyException exception]); + return; + } + + block(roomID, nil); + }]; objc_autoreleasePoolPop(pool); } @end Index: src/exceptions/MTXClientException.h ================================================================== --- src/exceptions/MTXClientException.h +++ src/exceptions/MTXClientException.h @@ -27,18 +27,19 @@ OF_ASSUME_NONNULL_BEGIN @class MTXClient; @interface MTXClientException: OFException -@property (readonly, nonatomic) MTXClient *client; @property (readonly, nonatomic) int statusCode; @property (readonly, nonatomic) mtx_response_t response; +@property (readonly, nonatomic) MTXClient *client; -+ (instancetype)exceptionWithClient: (MTXClient *)client - statusCode: (int)statusCode - response: (mtx_response_t)response; -- (instancetype)initWithClient: (OFString *)user - statusCode: (int)statusCode - response: (mtx_response_t)response; ++ (instancetype)exceptionWithStatusCode: (int)statusCode + response: (mtx_response_t)response + client: (MTXClient *)client; +- (instancetype)initWithStatusCode: (int)statusCode + response: (mtx_response_t)respons + client: (MTXClient *)client + OF_DESIGNATED_INITIALIZER; @end OF_ASSUME_NONNULL_END Index: src/exceptions/MTXClientException.m ================================================================== --- src/exceptions/MTXClientException.m +++ src/exceptions/MTXClientException.m @@ -23,29 +23,29 @@ #import "MTXLogoutFailedException.h" #import "MTXClient.h" @implementation MTXClientException -+ (instancetype)exceptionWithClient: (MTXClient *)client - statusCode: (int)statusCode - response: (mtx_response_t)response ++ (instancetype)exceptionWithStatusCode: (int)statusCode + response: (mtx_response_t)response + client: (MTXClient *)client { - return [[[self alloc] initWithClient: client - statusCode: statusCode - response: response] autorelease]; + return [[[self alloc] initWithStatusCode: statusCode + response: response + client: client] autorelease]; } -- (instancetype)initWithClient: (MTXClient *)client - statusCode: (int)statusCode - response: (mtx_response_t)response +- (instancetype)initWithStatusCode: (int)statusCode + response: (mtx_response_t)response + client: (MTXClient *)client { self = [super init]; @try { - _client = [client retain]; _statusCode = statusCode; _response = [response copy]; + _client = [client retain]; } @catch (id e) { [self release]; @throw e; } @@ -52,11 +52,11 @@ return self; } - (void)dealloc { - [_client release]; [_response release]; + [_client release]; [super dealloc]; } @end ADDED src/exceptions/MTXJoinRoomFailedException.h Index: src/exceptions/MTXJoinRoomFailedException.h ================================================================== --- /dev/null +++ src/exceptions/MTXJoinRoomFailedException.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 MTXJoinRoomFailedException: MTXClientException +@property (readonly, nonatomic) OFString *room; + ++ (instancetype)exceptionWithStatusCode: (int)statusCode + response: (mtx_response_t)response + client: (MTXClient *)client OF_UNAVAILABLE; ++ (instancetype)exceptionWithRoom: (OFString *)room + 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)initWithRoom: (OFString *)room + statusCode: (int)statusCode + response: (mtx_response_t)response + client: (MTXClient *)client; +@end + +OF_ASSUME_NONNULL_END ADDED src/exceptions/MTXJoinRoomFailedException.m Index: src/exceptions/MTXJoinRoomFailedException.m ================================================================== --- /dev/null +++ src/exceptions/MTXJoinRoomFailedException.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 "MTXJoinRoomFailedException.h" + +#import "MTXClient.h" + +@implementation MTXJoinRoomFailedException ++ (instancetype)exceptionWithRoom: (OFString *)room + statusCode: (int)statusCode + response: (mtx_response_t)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 + client: (MTXClient *)client +{ + self = [super initWithStatusCode: statusCode + response: response + client: client]; + + @try { + _room = [room copy]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_room release]; + + [super dealloc]; +} + +- (OFString *)description +{ + return [OFString stringWithFormat: + @"Failed to join room %@ for %@: %@", + _room, self.client.userID, self.response]; +} +@end Index: src/exceptions/Makefile ================================================================== --- src/exceptions/Makefile +++ src/exceptions/Makefile @@ -3,10 +3,11 @@ STATIC_PIC_LIB_NOINST = ${EXCEPTIONS_LIB_A} STATIC_LIB_NOINST = ${EXCEPTIONS_A} SRCS = MTXClientException.m \ MTXFetchRoomListFailedException.m \ + MTXJoinRoomFailedException.m \ MTXLoginFailedException.m \ MTXLogoutFailedException.m INCLUDES = ${SRCS:.m=.h} include ../../buildsys.mk Index: tests/tests.m ================================================================== --- tests/tests.m +++ tests/tests.m @@ -72,10 +72,25 @@ [OFApplication terminateWithStatus: 1]; } of_log(@"Fetched room list: %@", rooms); + [self joinRoom]; + }]; +} + +- (void)joinRoom +{ + [_client joinRoom: @"#test:nil.im" + block: ^ (OFString *roomID, id exception) { + if (exception != nil) { + of_log(@"Failed to join room: %@", exception); + [OFApplication terminateWithStatus: 1]; + } + + of_log(@"Joined room %@", roomID); + [self logOut]; }]; } - (void)logOut