From 4bbf0d9e3d27ee76c75c33a5050686c162336613 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 2 Mar 2018 10:04:15 -0500 Subject: [PATCH] Integrate with logs service. --- Pods | 2 +- Signal/src/AppDelegate.m | 3 + Signal/src/util/Pastelog.m | 204 ++++++++++++++++----------- SignalServiceKit/src/Util/OWSError.h | 3 +- 4 files changed, 127 insertions(+), 85 deletions(-) diff --git a/Pods b/Pods index 29babe215..54aac3475 160000 --- a/Pods +++ b/Pods @@ -1 +1 @@ -Subproject commit 29babe215072d52688ea5b18592f308bfc190612 +Subproject commit 54aac3475a78b5633781c440f2596da4c66ec6d9 diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 254dc626d..510492654 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -1112,6 +1112,9 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; [OWSPreferences setIsReadyForAppExtensions]; [self ensureRootViewController]; + + // TODO: Remove + [Pastelog submitLogs]; } - (void)registrationStateDidChange diff --git a/Signal/src/util/Pastelog.m b/Signal/src/util/Pastelog.m index f0d6fd907..b201b8cb4 100644 --- a/Signal/src/util/Pastelog.m +++ b/Signal/src/util/Pastelog.m @@ -5,6 +5,7 @@ #import "Pastelog.h" #import "Signal-Swift.h" #import "ThreadUtil.h" +#import #import #import #import @@ -14,6 +15,9 @@ #import #import +// TODO: Remove +#import "NSData+hexString.h" + NS_ASSUME_NONNULL_BEGIN typedef void (^UploadDebugLogsSuccess)(NSURL *url); @@ -26,9 +30,10 @@ typedef void (^UploadDebugLogsFailure)(NSString *localizedErrorMessage); typedef void (^DebugLogUploadSuccess)(DebugLogUploader *uploader, NSURL *url); typedef void (^DebugLogUploadFailure)(DebugLogUploader *uploader, NSError *error); -@interface DebugLogUploader : NSObject +@interface DebugLogUploader : NSObject -@property (nonatomic) NSMutableData *responseData; +@property (nonatomic) NSURL *fileUrl; +@property (nonatomic) NSString *mimeType; @property (nonatomic, nullable) DebugLogUploadSuccess success; @property (nonatomic, nullable) DebugLogUploadFailure failure; @@ -43,103 +48,135 @@ typedef void (^DebugLogUploadFailure)(DebugLogUploader *uploader, NSError *error DDLogVerbose(@"Dealloc: %@", self.logTag); } -- (void)uploadFileWithURL:(NSURL *)fileUrl success:(DebugLogUploadSuccess)success failure:(DebugLogUploadFailure)failure +- (void)uploadFileWithURL:(NSURL *)fileUrl + mimeType:(NSString *)mimeType + success:(DebugLogUploadSuccess)success + failure:(DebugLogUploadFailure)failure { OWSAssert(fileUrl); + OWSAssert(mimeType.length > 0); OWSAssert(success); OWSAssert(failure); + self.fileUrl = fileUrl; + self.mimeType = mimeType; self.success = success; self.failure = failure; - self.responseData = [NSMutableData new]; - - NSURL *url = [NSURL URLWithString:@"https://filebin.net"]; - - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url - cachePolicy:NSURLRequestReloadIgnoringLocalCacheData - timeoutInterval:30]; - [request setHTTPMethod:@"POST"]; - [request addValue:fileUrl.lastPathComponent forHTTPHeaderField:@"filename"]; - [request addValue:@"application/zip" forHTTPHeaderField:@"Content-Type"]; - NSData *_Nullable data = [NSData dataWithContentsOfURL:fileUrl]; - if (!data) { - [self failWithError:[NSError errorWithDomain:@"PastelogKit" - code:10002 - userInfo:@{ NSLocalizedDescriptionKey : @"Could not load data." }]]; - return; - } - // TODO: - [request setHTTPBody:data]; - NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self]; - [connection start]; -} + // TODO: Remove + NSData *data = [NSData dataWithContentsOfURL:fileUrl]; + DDLogInfo(@"%@ data: %zd", self.logTag, data.length); + NSData *header = [data subdataWithRange:NSMakeRange(0, MIN((NSUInteger)256, data.length))]; + NSString *hexString = [header hexadecimalString]; + DDLogInfo(@"%@ hexString: %@", self.logTag, hexString); -#pragma mark - Delegate Methods + [self getUploadParameters]; +} -- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +- (void)getUploadParameters { - DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); - - [self.responseData appendData:data]; + __weak DebugLogUploader *weakSelf = self; + + // TODO: Remove + // The JSON object it returns has two elements, URL, and "fields". Just POST to "ur" with a multipart/form-data body + // that has each KV pair in "fields" encoded as a form element. Add your file, called "file", and what you post will + // be at debuglogs.org/fields['key'] + + NSURLSessionConfiguration *sessionConf = NSURLSessionConfiguration.ephemeralSessionConfiguration; + AFHTTPSessionManager *sessionManager = + [[AFHTTPSessionManager alloc] initWithBaseURL:nil sessionConfiguration:sessionConf]; + sessionManager.requestSerializer = [AFHTTPRequestSerializer serializer]; + sessionManager.responseSerializer = [AFJSONResponseSerializer serializer]; + NSString *urlString = @"https://debuglogs.org/"; + [sessionManager GET:urlString + parameters:nil + progress:nil + success:^(NSURLSessionDataTask *task, id _Nullable responseObject) { + if (![responseObject isKindOfClass:[NSDictionary class]]) { + DDLogError(@"%@ Invalid response: %@, %@", weakSelf.logTag, urlString, responseObject); + [weakSelf + failWithError:OWSErrorWithCodeDescription(OWSErrorCodeDebugLogUploadFailed, @"Invalid response")]; + return; + } + NSString *uploadUrl = responseObject[@"url"]; + if (![uploadUrl isKindOfClass:[NSString class]] || uploadUrl.length < 1) { + DDLogError(@"%@ Invalid response: %@, %@", weakSelf.logTag, urlString, responseObject); + [weakSelf + failWithError:OWSErrorWithCodeDescription(OWSErrorCodeDebugLogUploadFailed, @"Invalid response")]; + return; + } + NSDictionary *fields = responseObject[@"fields"]; + if (![fields isKindOfClass:[NSDictionary class]] || fields.count < 1) { + DDLogError(@"%@ Invalid response: %@, %@", weakSelf.logTag, urlString, responseObject); + [weakSelf + failWithError:OWSErrorWithCodeDescription(OWSErrorCodeDebugLogUploadFailed, @"Invalid response")]; + return; + } + for (NSString *fieldName in fields) { + NSString *fieldValue = fields[fieldName]; + if (![fieldName isKindOfClass:[NSString class]] || fieldName.length < 1 + || ![fieldValue isKindOfClass:[NSString class]] || fieldValue.length < 1) { + DDLogError(@"%@ Invalid response: %@, %@", weakSelf.logTag, urlString, responseObject); + [weakSelf failWithError:OWSErrorWithCodeDescription( + OWSErrorCodeDebugLogUploadFailed, @"Invalid response")]; + return; + } + } + NSString *_Nullable uploadKey = fields[@"key"]; + if (![uploadKey isKindOfClass:[NSString class]] || uploadKey.length < 1) { + DDLogError(@"%@ Invalid response: %@, %@", weakSelf.logTag, urlString, responseObject); + [weakSelf + failWithError:OWSErrorWithCodeDescription(OWSErrorCodeDebugLogUploadFailed, @"Invalid response")]; + return; + } + [weakSelf uploadFileWithUploadUrl:uploadUrl fields:fields uploadKey:uploadKey]; + } + failure:^(NSURLSessionDataTask *_Nullable task, NSError *error) { + DDLogError(@"%@ failed: %@", weakSelf.logTag, urlString); + [weakSelf failWithError:error]; + }]; } -- (void)connectionDidFinishLoading:(NSURLConnection *)connection +- (void)uploadFileWithUploadUrl:(NSString *)uploadUrl fields:(NSDictionary *)fields uploadKey:(NSString *)uploadKey { - DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); - - NSError *error; - NSDictionary *_Nullable dict = [NSJSONSerialization JSONObjectWithData:self.responseData options:0 error:&error]; - if (error) { - DDLogError(@"%@ response length: %zd", self.logTag, self.responseData.length); - [self failWithError:error]; - return; - } - - if (![dict isKindOfClass:[NSDictionary class]]) { - DDLogError(@"%@ response (1): %@", self.logTag, dict); - [self failWithError:[NSError errorWithDomain:@"PastelogKit" - code:10003 - userInfo:@{ NSLocalizedDescriptionKey : @"Malformed response (root)." }]]; - return; - } - - NSArray *_Nullable links = [dict objectForKey:@"links"]; - if (![links isKindOfClass:[NSArray class]]) { - DDLogError(@"%@ response (2): %@", self.logTag, dict); - [self failWithError:[NSError errorWithDomain:@"PastelogKit" - code:10004 - userInfo:@{ NSLocalizedDescriptionKey : @"Malformed response (links)." }]]; - return; - } - NSString *_Nullable urlString = nil; - for (NSDictionary *linkMap in links) { - if (![linkMap isKindOfClass:[NSDictionary class]]) { - DDLogError(@"%@ response (2): %@", self.logTag, dict); - [self failWithError:[NSError - errorWithDomain:@"PastelogKit" - code:10005 - userInfo:@{ NSLocalizedDescriptionKey : @"Malformed response (linkMap)." }]]; - return; - } - NSString *_Nullable linkRel = [linkMap objectForKey:@"rel"]; - if (![linkRel isKindOfClass:[NSString class]]) { - DDLogError(@"%@ response (linkRel): %@", self.logTag, dict); - continue; - } - if (![linkRel isEqualToString:@"file"]) { - DDLogError(@"%@ response (linkRel value): %@", self.logTag, dict); - continue; + OWSAssert(uploadUrl.length > 0); + OWSAssert(fields); + OWSAssert(uploadKey.length > 0); + + __weak DebugLogUploader *weakSelf = self; + NSURLSessionConfiguration *sessionConf = NSURLSessionConfiguration.ephemeralSessionConfiguration; + AFHTTPSessionManager *sessionManager = + [[AFHTTPSessionManager alloc] initWithBaseURL:nil sessionConfiguration:sessionConf]; + sessionManager.requestSerializer = [AFHTTPRequestSerializer serializer]; + sessionManager.responseSerializer = [AFJSONResponseSerializer serializer]; + [sessionManager POST:uploadUrl + parameters:@{} + constructingBodyWithBlock:^(id formData) { + for (NSString *fieldName in fields) { + NSString *fieldValue = fields[fieldName]; + [formData appendPartWithFormData:[fieldValue dataUsingEncoding:NSUTF8StringEncoding] name:fieldName]; + } + NSError *error; + BOOL success = [formData appendPartWithFileURL:weakSelf.fileUrl + name:@"file" + fileName:weakSelf.fileUrl.lastPathComponent + mimeType:weakSelf.mimeType + error:&error]; + if (!success || error) { + DDLogError(@"%@ failed: %@, error: %@", weakSelf.logTag, uploadUrl, error); + } } - NSString *_Nullable linkHref = [linkMap objectForKey:@"href"]; - if (![linkHref isKindOfClass:[NSString class]]) { - DDLogError(@"%@ response (linkHref): %@", self.logTag, dict); - continue; + progress:nil + success:^(NSURLSessionDataTask *task, id _Nullable responseObject) { + DDLogVerbose(@"%@ Response: %@, %@", weakSelf.logTag, uploadUrl, responseObject); + + NSString *urlString = [NSString stringWithFormat:@"https://debuglogs.org/%@", uploadKey]; + [self succeedWithUrl:[NSURL URLWithString:urlString]]; } - urlString = linkHref; - break; - } - [self succeedWithUrl:[NSURL URLWithString:urlString]]; + failure:^(NSURLSessionDataTask *_Nullable task, NSError *error) { + DDLogError(@"%@ failed: %@", weakSelf.logTag, uploadUrl); + [weakSelf failWithError:error]; + }]; } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response @@ -383,6 +420,7 @@ typedef void (^DebugLogUploadFailure)(DebugLogUploader *uploader, NSError *error __weak Pastelog *weakSelf = self; self.currentUploader = [DebugLogUploader new]; [self.currentUploader uploadFileWithURL:[NSURL fileURLWithPath:zipFilePath] + mimeType:@"application/zip" success:^(DebugLogUploader *uploader, NSURL *url) { if (uploader != weakSelf.currentUploader) { // Ignore events from obsolete uploaders. diff --git a/SignalServiceKit/src/Util/OWSError.h b/SignalServiceKit/src/Util/OWSError.h index 828d5401f..07a45610e 100644 --- a/SignalServiceKit/src/Util/OWSError.h +++ b/SignalServiceKit/src/Util/OWSError.h @@ -28,7 +28,8 @@ typedef NS_ENUM(NSInteger, OWSErrorCode) { OWSErrorCodeCouldNotWriteAttachmentData = 777409, OWSErrorCodeMessageDeletedBeforeSent = 777410, OWSErrorCodeDatabaseConversionFatalError = 777411, - OWSErrorCodeMoveFileToSharedDataContainerError = 777412 + OWSErrorCodeMoveFileToSharedDataContainerError = 777412, + OWSErrorCodeDebugLogUploadFailed = 777414, }; extern NSString *const OWSErrorRecipientIdentifierKey;