From 9d78e7d654c51c22ce4be87efef5bc1f8d5a2d08 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 31 Oct 2019 16:16:48 +0300 Subject: Fix authentication --- Juick/API.m | 314 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 Juick/API.m (limited to 'Juick/API.m') diff --git a/Juick/API.m b/Juick/API.m new file mode 100644 index 0000000..4f57dc6 --- /dev/null +++ b/Juick/API.m @@ -0,0 +1,314 @@ +// +// API.m +// Juick +// +// Created by Vitaly Takmazov on 22/08/16. +// Copyright © 2016 com.juick. All rights reserved. +// +#import "Message.h" +#import "Chat.h" +#import "API.h" + +@interface API () +@property(nonatomic, strong) NSOperationQueue *backgroundQueue; +@property(nonatomic, strong) NSURL *baseURL; +@property(nonatomic, strong) NSURLSession *urlSession; + +-(void) get:(NSString *)path params:(NSDictionary *)params callback:(void(^)(NSDictionary *, NSError *))callback; +-(NSString *) authorizationHeader; + +@end + +@implementation API + +-(id)init { + if (self = [super init]) { + NSString *baseURLString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"base_url"]; + NSLog(@"Initializing with %@ base URL", baseURLString); + self.baseURL = [NSURL URLWithString:baseURLString]; + self.backgroundQueue = [NSOperationQueue new]; + self.urlSession = [NSURLSession sharedSession]; + } + return self; +} + +- (NSString *) authorizationHeader { + NSString *token = [[NSUserDefaults standardUserDefaults] stringForKey:@"token"]; + if (token) { + return [NSString stringWithFormat:@"Juick %@", token]; + } + return nil; +} + +- (void)authenticateWithUser:(NSString *)username password:(NSString *)password callback:(void(^)(BOOL))completed { + NSURL *url = [NSURL URLWithString:@"me"relativeToURL:self.baseURL]; + NSString *basicAuthString = [NSString stringWithFormat:@"Basic %@", [[[NSString stringWithFormat:@"%@:%@", username, password] dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]]; + + [self fetchDataWithURL:url data:nil boundary:nil authorizationHeader:basicAuthString callback:^(NSData *response, NSError *err) { + if (!err) { + NSError *jsonError; + NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:response options:0 error:&jsonError]; + if (!jsonError) { + User *me = [User fromJSON:jsonData]; + [[NSUserDefaults standardUserDefaults] setValue:me.token forKeyPath:@"token"]; + self.currentUser = me; + completed(YES); + } + } + completed(NO); + }]; +} + +- (void)signout { + self.currentUser = nil; + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"token"]; +} + +-(void) pullNextFromPath:(NSString *)path params:(NSDictionary *) params callback:(void(^)(NSArray *, NSError *))callback { + [self get:path params:params callback:^(NSDictionary *response, NSError *error) { + NSMutableArray *messages = [NSMutableArray new]; + if (!error) { + [((NSArray *)response) enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + [messages addObject:[Message fromJSON:obj]]; + }]; + } + callback(messages, error); + }]; +} + +- (void)me:(void (^)(User *, NSError *error))callback { + [self get:@"me" params:nil callback:^(NSDictionary *response, NSError *err) { + User *visitor; + if (!err) { + visitor = [User fromJSON:response]; + self.currentUser = visitor; + } + callback(visitor, err); + }]; +} + +-(void) postMessage:(NSString *)text result:(void (^)(Message *, NSError *))callback { + [self post:@"post" params:@{ + @"body": text + } callback:^(NSDictionary *response, NSError *err) { + Message *result; + if (!err) { + result = [Message fromJSON:response[@"newMessage"]]; + } + callback(result, err); + }]; +} + +-(void) postReplyToThread:(NSNumber *)mid inReplyTo:(NSNumber *)rid text:(NSString *)text result:(void(^)(Message *, NSError *))callback { + [self post:@"comment" params:@{ + @"mid": mid, + @"rid": [NSString stringWithFormat:@"%ld", [rid integerValue]], + @"body": text + } callback:^(NSDictionary *response, NSError *err) { + Message *reply; + if (!err) { + NSLog(@"Success!"); + reply = [Message fromJSON:response]; + } + callback(reply, err); + }]; +} + +-(void) postPMToUser:(NSString *)uname text:(NSString *)text result:(void (^)(NSError *))callback { + [self.backgroundQueue addOperationWithBlock:^{ + [self post:@"pm" params:@{ + @"uname": uname, + @"body": text + } callback:^(NSDictionary *response, NSError *err) { + callback(err); + }]; + }]; +} + +-(void) fetchChats:(void (^)(NSArray *, NSError *))callback { + [self get:@"groups_pms" params:nil callback:^(NSDictionary *response, NSError *err) { + NSMutableArray *groups = [NSMutableArray new]; + NSArray *pms = [response objectForKey:@"pms"]; + [pms enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + [groups addObject:[Chat fromJSON:obj]]; + }]; + callback(groups, err); + }]; +} + +-(void) fetchChatWithUser:(NSString *)uname callback:(void (^)(NSArray *, NSError *))callback { + NSDictionary *params = @{@"uname": uname}; + [self get:@"pm" params:params callback:^(NSDictionary *response, NSError *err) { + NSMutableArray *messages = [NSMutableArray new]; + if (!err) { + NSArray *messagesList = (NSArray *)response; + [messagesList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + [messages addObject:[Message fromJSON:obj]]; + }]; + } + callback(messages, err); + }]; +} + +-(void) authenticate:(void (^)(User *user, NSError *error))callback { + [self get:@"me" params:nil callback:^(NSDictionary *response, NSError *error) { + User *user; + if (!error) { + user = [User fromJSON:response]; + } + callback(user, error); + }]; +} + +-(void) getUserByName:(NSString *) name callback:(void(^)(User *))callback { + NSDictionary *params = @{@"uname": name}; + [self get:@"users" params:params callback:^(NSDictionary *response, NSError *err) { + if (!err) { + callback([User fromJSON:[(NSArray *)response firstObject]]); + } else { + callback(nil); + } + }]; +} + +-(void) refreshDeviceRegistration:(DeviceRegistration *)registrationData callback:(void (^)(BOOL))callback { + NSURL *notificationsUrl = [NSURL URLWithString:@"notifications" relativeToURL:self.baseURL]; + NSError *error; + NSDictionary *token = [registrationData toJSON]; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:@[token] options:kNilOptions error:&error]; + [self fetchDataWithURL:notificationsUrl data:jsonData boundary:nil authorizationHeader:self.authorizationHeader callback:^(NSData *response, NSError *err) { + callback(!err); + }]; +} + +-(void) fetchDataWithURL:(NSURL *) url data:(NSData *)postData boundary:(NSString *)boundary authorizationHeader:(NSString *)header callback:(void(^)(NSData *, NSError *))callback { + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + if (header) { + [request addValue:header forHTTPHeaderField:@"Authorization"]; + } + if (boundary) { + request.HTTPMethod = @"POST"; + request.HTTPBody = postData; + NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]; + [request setValue:contentType forHTTPHeaderField: @"Content-Type"]; + } else if (postData) { + request.HTTPMethod = @"PUT"; + request.HTTPBody = postData; + [request setValue:@"application/json" forHTTPHeaderField: @"Content-Type"]; + } else { + request.HTTPMethod = @"GET"; + } + + NSURLSessionDataTask *task = [self.urlSession dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { + if (error) { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + callback(nil, error); + }]; + } else { + NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode; + if (!response || statusCode != 200) { + NSURL *url = request.URL; + NSError *err = [NSError errorWithDomain:@"JuickErrorDomain" + code:statusCode + userInfo:@{@"url": url.absoluteString}]; + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + callback(nil, err); + }]; + } else { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + callback(data, error); + }]; + } + } + }]; + [self.backgroundQueue addOperationWithBlock:^{ + [task resume]; + }]; +} + +-(NSURL *) url:(NSURL *)url withParameters:(NSDictionary *)params { + NSURLComponents *parametersUrl = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:YES]; + NSMutableArray *items = [NSMutableArray array]; + for (NSString * key in params) { + NSString *value = [NSString stringWithFormat:@"%@", params[key]]; + [items addObject:[NSURLQueryItem queryItemWithName:key value:value]]; + } + [parametersUrl setQueryItems:items]; + return [parametersUrl URL]; +} + +-(NSData *) multipartData:(NSDictionary *)params withBoundary:(NSString *)boundary { + NSMutableData *body = [NSMutableData data]; + for (NSString *key in params) { + [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=%@\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[[NSString stringWithFormat:@"%@\r\n", params[key]] dataUsingEncoding:NSUTF8StringEncoding]]; + } + [body appendData:[[NSString stringWithFormat:@"--%@--", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + return body; +} + +-(void) get:(NSString *) path params:(NSDictionary *)params callback:(void(^)(NSDictionary *, NSError *))callback { + NSURL *url = [NSURL URLWithString:path relativeToURL:self.baseURL]; + NSURL *requestUrl = params ? [self url:url withParameters:params] : url; + [self fetchDataWithURL:requestUrl data:nil boundary:nil authorizationHeader:self.authorizationHeader callback:^(NSData *data, NSError *err) { + if (err) { + callback(nil, err); + } else { + NSError *jsonError; + NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; + if (jsonError) { + callback(nil, jsonError); + } else { + callback(jsonData, nil); + } + } + }]; +} + +-(void) post:(NSString *) path params:(NSDictionary *)params callback:(void(^)(NSDictionary *, NSError *))callback { + NSURL *url = [NSURL URLWithString:path relativeToURL:self.baseURL]; + NSString *boundary = [NSString stringWithFormat:@"Boundary-%@", [[NSUUID UUID] UUIDString]]; + [self fetchDataWithURL:url data:[self multipartData:params withBoundary:boundary] boundary:boundary + authorizationHeader:self.authorizationHeader callback:^(NSData *data, NSError *err) { + if (!err) { + NSError *jsonError; + NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; + if (jsonError) { + callback(nil, jsonError); + } else { + callback(jsonData, nil); + } + } else { + callback(nil, err); + } + }]; +} + +-(void) fetchImageWithURL:(NSURL *) url callback:(void(^)(NSData *))callback { + [self fetchDataWithURL:url data:nil boundary:nil authorizationHeader:self.authorizationHeader callback:^(NSData *data, NSError *err) { + if (err) { + callback(nil); + } else { + callback(data); + } + }]; +} + ++(NSString *) messagesUrl { + return @"messages"; +} + ++(NSString *) discussionsUrl { + return @"messages/discussions"; +} + ++(NSString *) threadUrl { + return @"thread"; +} + ++(NSString *) feedUrl { + return @"home"; +} + +@end -- cgit v1.2.3