Index: .fossil-settings/ignore-glob ================================================================== --- .fossil-settings/ignore-glob +++ .fossil-settings/ignore-glob @@ -15,5 +15,6 @@ buildsys.mk config.log config.status configure extra.mk +tests/tests Index: extra.mk.in ================================================================== --- extra.mk.in +++ extra.mk.in @@ -1,13 +1,14 @@ 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} 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@ RUN_TESTS = @RUN_TESTS@ ADDED src/MTXClient.h Index: src/MTXClient.h ================================================================== --- src/MTXClient.h +++ src/MTXClient.h @@ -0,0 +1,97 @@ +/* + * 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 + +OF_ASSUME_NONNULL_BEGIN + +@class MTXClient; + +/** + * @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, + id _Nullable exception); + +/** + * @brief A class that represents a client. + */ +@interface MTXClient: OFObject +/** + * @brief The user ID used by the client. + */ +@property (readonly, nonatomic) OFString *userID; + +/** + * @brief The device ID used by the client. + */ +@property (readonly, nonatomic) OFString *deviceID; + +/** + * @brief The access token used by the client. + */ +@property (readonly, nonatomic) OFString *accessToken; + +/** + * @brief The homeserver used by the client. + */ +@property (readonly, nonatomic) OFURL *homeserver; + +/** + * @brief Creates a new client with the specified access token on the specified + * homeserver. + * + * @param accessToken The access token for the client + * @param homeserver The URL of the homeserver + * @return An autoreleased MTXClient + */ ++ (instancetype)clientWithUserID: (OFString *)userID + deviceID: (OFString *)deviceID + accessToken: (OFString *)accessToken + homeserver: (OFURL *)homeserver; + +/** + * @brief Logs into the homeserver and creates a new client. + */ ++ (void)logInWithUser: (OFString *)user + password: (OFString *)password + homeserver: (OFURL *)homeserver + block: (mtx_client_login_block_t)block; + +/** + * @brief Initializes an already allocated client with the specified access + * token on the specified homeserver. + * + * @param accessToken The access token for the client + * @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 ADDED src/MTXClient.m Index: src/MTXClient.m ================================================================== --- src/MTXClient.m +++ src/MTXClient.m @@ -0,0 +1,173 @@ +/* + * 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 "MTXClient.h" +#import "MTXRequest.h" + +#import "MTXLoginFailedException.h" + +static void +validateHomeserver(OFURL *homeserver) +{ + if (![homeserver.scheme isEqual: @"http"] && + ![homeserver.scheme isEqual: @"https"]) + @throw [OFUnsupportedProtocolException + exceptionWithURL: homeserver]; + + if (homeserver.path != nil && ![homeserver.path isEqual: @"/"]) + @throw [OFInvalidArgumentException exception]; + + if (homeserver.user != nil || homeserver.password != nil || + homeserver.query != nil || homeserver.fragment != nil) + @throw [OFInvalidArgumentException exception]; +} + +@implementation MTXClient ++ (instancetype)clientWithUserID: (OFString *)userID + deviceID: (OFString *)deviceID + accessToken: (OFString *)accessToken + homeserver: (OFURL *)homeserver +{ + return [[[self alloc] initWithUserID: userID + deviceID: deviceID + accessToken: accessToken + homeserver: homeserver] autorelease]; +} + ++ (void)logInWithUser: (OFString *)user + password: (OFString *)password + homeserver: (OFURL *)homeserver + block: (mtx_client_login_block_t)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.body = @{ + @"type": @"m.login.password", + @"identifier": @{ + @"type": @"m.id.user", + @"user": user + }, + @"password": password + }; + + [request asyncPerformWithBlock: + ^ (OFDictionary *response, int statusCode, + id exception) { + if (exception != nil) { + block(nil, exception); + return; + } + + if (statusCode != 200) { + id exception = [MTXLoginFailedException + exceptionWithUser: user + homeserver: homeserver + statusCode: statusCode + response: response]; + block(nil, exception); + return; + } + + OFString *userID = response[@"user_id"]; + OFString *deviceID = response[@"device_id"]; + OFString *accessToken = response[@"access_token"]; + if (userID == nil || deviceID == nil || + accessToken == nil) { + block(nil, [OFInvalidServerReplyException exception]); + return; + } + + OFString *baseURL = + response[@"well_known"][@"m.homeserver"][@"base_url"]; + OFURL *realHomeserver; + if (baseURL != nil) { + @try { + realHomeserver = [OFURL URLWithString: baseURL]; + } @catch (id e) { + block(nil, e); + return; + } + } else + realHomeserver = homeserver; + + MTXClient *client = [MTXClient + clientWithUserID: userID + deviceID: deviceID + accessToken: accessToken + homeserver: realHomeserver]; + block(client, nil); + }]; + + objc_autoreleasePoolPop(pool); +} + +- (instancetype)initWithUserID: (OFString *)userID + deviceID: (OFString *)deviceID + accessToken: (OFString *)accessToken + homeserver: (OFURL *)homeserver +{ + self = [super init]; + + @try { + validateHomeserver(homeserver); + + _userID = [userID copy]; + _deviceID = [deviceID copy]; + _accessToken = [accessToken copy]; + _homeserver = [homeserver copy]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_userID release]; + [_deviceID release]; + [_accessToken release]; + [_homeserver release]; + + [super dealloc]; +} + +- (OFString *)description +{ + return [OFString stringWithFormat: + @"<%@\n" + @"\tUser ID = %@\n" + @"\tDevice ID = %@\n" + @"\tAccess token = %@\n" + @"\tHomeserver = %@\n" + @">", + self.class, _userID, _deviceID, _accessToken, _homeserver]; +} +@end ADDED src/MTXRequest.h Index: src/MTXRequest.h ================================================================== --- src/MTXRequest.h +++ src/MTXRequest.h @@ -0,0 +1,106 @@ +/* + * 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 + +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 *_Nullable response, int statusCode, + id _Nullable exception); + +/** + * @brief An internal class for performing a request on the Matrix server. + */ +@interface MTXRequest: OFObject +/** + * @brief The access token to use. + * + * Some requests are unauthenticated - for those, the access token is `nil`. + */ +@property (readonly, nonatomic, nullable) OFString *accessToken; + +/** + * @brief The URL of the homeserver to send the request to. + */ +@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; + +/** + * @brief The path of the request. + */ +@property (copy, nonatomic) OFString *path; + +/** + * @brief An optional body to send along with the request. + * + * This is a dictionary that gets serialized to JSON when the request is sent. + */ +@property (copy, nullable, nonatomic) OFDictionary *body; + +/** + * @brief Creates a new request with the specified access token and homeserver. + * + * @param accessToken An (optional) access token to use + * @param homeserver The homeserver the request will be sent to + * @return An autoreleased MTXRequest + */ ++ (instancetype)requestWithPath: (OFString *)path + accessToken: (nullable OFString *)accessToken + homeserver: (OFURL *)homeserver; + +/** + * @brief Initializes an already allocated request with the specified access + * token and homeserver. + * + * @param accessToken An (optional) access token to use + * @param homeserver The homeserver the request will be sent to + * @return An initialized MTXRequest + */ +- (instancetype)initWithPath: (OFString *)path + accessToken: (nullable OFString *)accessToken + homeserver: (OFURL *)homeserver; + +/** + * @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)asyncPerformWithBlock: (mtx_request_block_t)block; +@end + +OF_ASSUME_NONNULL_END ADDED src/MTXRequest.m Index: src/MTXRequest.m ================================================================== --- src/MTXRequest.m +++ src/MTXRequest.m @@ -0,0 +1,176 @@ +/* + * 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 "MTXRequest.h" + +@implementation MTXRequest +{ + OFData *_body; + mtx_request_block_t _block; +} + ++ (instancetype)requestWithPath: (OFString *)path + accessToken: (OFString *)accessToken + homeserver: (OFURL *)homeserver +{ + return [[[self alloc] initWithPath: path + accessToken: accessToken + homeserver: homeserver] autorelease]; +} + +- (instancetype)initWithPath: (OFString *)path + accessToken: (OFString *)accessToken + homeserver: (OFURL *)homeserver +{ + self = [super init]; + + @try { + _accessToken = [accessToken copy]; + _homeserver = [homeserver copy]; + _path = [path copy]; + _method = OF_HTTP_REQUEST_METHOD_GET; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_accessToken release]; + [_homeserver release]; + [_path release]; + [_body release]; + + [super dealloc]; +} + +- (void)setBody: (OFDictionary *)body +{ + void *pool = objc_autoreleasePoolPush(); + + [_body release]; + + OFString *JSONString = [body JSONRepresentation]; + _body = [[OFData alloc] + initWithItems: JSONString.UTF8String + count: JSONString.UTF8StringLength]; + + objc_autoreleasePoolPop(pool); +} + +- (OFDictionary *)body +{ + return [OFString stringWithUTF8String: _body.items + length: _body.count] + .objectByParsingJSON; +} + +- (void)asyncPerformWithBlock: (mtx_request_block_t)block +{ + void *pool = objc_autoreleasePoolPush(); + + if (_block != nil) + /* Not the best exception to indicate it's already in-flight. */ + @throw [OFAlreadyConnectedException exception]; + + 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; + + OFHTTPClient *client = [OFHTTPClient client]; + client.delegate = self; + + _block = [block copy]; + [self retain]; + [client asyncPerformRequest: request]; + + objc_autoreleasePoolPop(pool); +} + +- (void)client: (OFHTTPClient *)client + didPerformRequest: (OFHTTPRequest *)request + response: (OFHTTPResponse *)response +{ + /* Reset to nil first, so that another one can be performed. */ + mtx_request_block_t block = _block; + _block = nil; + + @try { + OFMutableData *responseData = [OFMutableData data]; + while (!response.atEndOfStream) { + char buffer[512]; + size_t length = [response readIntoBuffer: buffer + length: 512]; + + [responseData addItems: buffer + count: length]; + } + + OFDictionary *responseJSON = + [OFString stringWithUTF8String: responseData.items + length: responseData.count] + .objectByParsingJSON; + + block(responseJSON, response.statusCode, nil); + } @catch (id e) { + block(nil, response.statusCode, e); + } + + [block release]; + [self release]; +} + +- (void)client: (OFHTTPClient *)client + didFailWithException: (id)exception + request: (OFHTTPRequest *)request +{ + /* Reset to nil first, so that another one can be performed. */ + mtx_request_block_t block = _block; + _block = nil; + + block(nil, 0, exception); + + [block release]; + [self release]; +} + +- (void)client: (OFHTTPClient *)client + wantsRequestBody: (OFStream *)body + request: (OFHTTPRequest *)request +{ + [body writeData: _body]; +} +@end Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -6,10 +6,12 @@ STATIC_LIB = ${OBJMATRIX_STATIC_LIB} FRAMEWORK = ${OBJMATRIX_FRAMEWORK} LIB_MAJOR = ${OBJMATRIX_LIB_MAJOR} LIB_MINOR = ${OBJMATRIX_LIB_MINOR} +SRCS = MTXClient.m \ + MTXRequest.m INCLUDES := ${SRCS:.m=.h} \ ObjMatrix.h OBJS_EXTRA = ${EXCEPTIONS_EXCEPTIONS_A} LIB_OBJS_EXTRA = ${EXCEPTIONS_EXCEPTIONS_LIB_A} ADDED src/ObjMatrix.h Index: src/ObjMatrix.h ================================================================== --- src/ObjMatrix.h +++ src/ObjMatrix.h @@ -0,0 +1,24 @@ +/* + * 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 "MTXClient.h" +#import "MTXRequest.h" ADDED src/exceptions/MTXLoginFailedException.h Index: src/exceptions/MTXLoginFailedException.h ================================================================== --- src/exceptions/MTXLoginFailedException.h +++ src/exceptions/MTXLoginFailedException.h @@ -0,0 +1,43 @@ +/* + * 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 + +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; + ++ (instancetype)exceptionWithUser: (OFString *)user + homeserver: (OFURL *)homeserver + statusCode: (int)statusCode + response: (OFDictionary *)response; +- (instancetype)initWithUser: (OFString *)user + homeserver: (OFURL *)homeserver + statusCode: (int)statusCode + response: (OFDictionary *)response; +@end + +OF_ASSUME_NONNULL_END ADDED src/exceptions/MTXLoginFailedException.m Index: src/exceptions/MTXLoginFailedException.m ================================================================== --- src/exceptions/MTXLoginFailedException.m +++ src/exceptions/MTXLoginFailedException.m @@ -0,0 +1,65 @@ +/* + * 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 "MTXLoginFailedException.h" + +@implementation MTXLoginFailedException ++ (instancetype)exceptionWithUser: (OFString *)user + homeserver: (OFURL *)homeserver + statusCode: (int)statusCode + response: (OFDictionary *)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 *)response +{ + self = [super init]; + + @try { + _user = [user copy]; + _homeserver = [homeserver copy]; + _statusCode = statusCode; + _response = [response copy]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_user release]; + [_homeserver release]; + [_response release]; + + [super dealloc]; +} +@end Index: src/exceptions/Makefile ================================================================== --- src/exceptions/Makefile +++ src/exceptions/Makefile @@ -1,11 +1,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.. ADDED tests/Makefile Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -0,0 +1,45 @@ +PROG_NOINST = tests${PROG_SUFFIX} +SRCS = Tests.m + +include ../buildsys.mk +include ../extra.mk + +post-all: ${RUN_TESTS} + +.PHONY: run +run: + rm -f libobjmatrix.so.${OBJMATRIX_LIB_MAJOR} + rm -f libobjmatrix.so.${OBJMATRIX_LIB_MAJOR_MINOR} + rm -f objmatrix.dll libobjmatrix.${OBJMATRIX_LIB_MAJOR}.dylib + if test -f ../src/libobjmatrix.so; then \ + ${LN_S} ../src/libobjmatrix.so \ + libobjmatrix.so.${OBJMATRIX_LIB_MAJOR}; \ + ${LN_S} ../src/libobjmatrix.so \ + libobjmatrix.so.${OBJMATRIX_LIB_MAJOR_MINOR}; \ + elif test -f ../src/libobjmatrix.so.${OBJMATRIX_LIB_MAJOR_MINOR}; \ + then \ + ${LN_S} ../src/libobjmatrix.so.${OBJMATRIX_LIB_MAJOR_MINOR} \ + libobjmatrix.so.${OBJMATRIX_LIB_MAJOR_MINOR}; \ + fi + if test -f ../src/objmatrix.dll; then \ + ${LN_S} ../src/objmatrix.dll objmatrix.dll; \ + fi + if test -f ../src/libobjmatrix.dylib; then \ + ${LN_S} ../src/libobjmatrix.dylib \ + libobjmatrix.${OBJMATRIX_LIB_MAJOR}.dylib; \ + fi + LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH+:}$$LD_LIBRARY_PATH \ + DYLD_FRAMEWORK_PATH=../src:../src/runtime$${DYLD_FRAMEWORK_PATH+:}$$DYLD_FRAMEWORK_PATH \ + DYLD_LIBRARY_PATH=.$${DYLD_LIBRARY_PATH+:}$$DYLD_LIBRARY_PATH \ + LIBRARY_PATH=.$${LIBRARY_PATH+:}$$LIBRARY_PATH \ + ${WRAPPER} ./${PROG_NOINST}; EXIT=$$?; \ + rm -f libobjmatrix.so.${OBJMATRIX_LIB_MAJOR}; \ + rm -f libobjmatrix.so.${OBJMATRIX_LIB_MAJOR_MINOR} objmatrix.dll; \ + rm -f libobjmatrix.${OBJMATRIX_LIB_MAJOR}.dylib; \ + exit $$EXIT + +${PROG_NOINST}: ${LIBOBJMATRIX_DEP} + +CPPFLAGS += -I../src +LIBS := -L../src -lobjmatrix ${LIBS} +LD = ${OBJC} ADDED tests/tests.m Index: tests/tests.m ================================================================== --- tests/tests.m +++ tests/tests.m @@ -0,0 +1,60 @@ +/* + * 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 "MTXClient.h" + +@interface Tests: OFObject +@end + +OF_APPLICATION_DELEGATE(Tests) + +@implementation Tests +- (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"]; + [OFApplication terminateWithStatus: 1]; + } + + 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]; + [OFApplication terminateWithStatus: 1]; + } + + [of_stdout writeFormat: @"Logged in client: %@\n", client]; + [OFApplication terminate]; + }]; +} +@end