diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 4de216e56..dcb09e8bb 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -324,7 +324,7 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; [controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"SETTINGS_ADVANCED_SUBMIT_DEBUGLOG", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *_Nonnull action) { - [Pastelog submitLogsWithShareCompletion:^{ + [Pastelog submitLogsWithCompletion:^{ DDLogInfo( @"%@ exiting after sharing debug logs.", self.logTag); [DDLog flushLog]; diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index 1cce5a50e..916c70d30 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -7,7 +7,6 @@ #import "OWSTableViewController.h" #import "Signal-Swift.h" #import "ThreadUtil.h" -#import #import #import #import diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMisc.m b/Signal/src/ViewControllers/DebugUI/DebugUIMisc.m index 41ecfa7e9..40eb0cce6 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMisc.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMisc.m @@ -10,7 +10,6 @@ #import "RegistrationViewController.h" #import "Signal-Swift.h" #import "ThreadUtil.h" -#import #import #import #import diff --git a/Signal/src/ViewControllers/DebugUI/DebugUISyncMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUISyncMessages.m index 51cb890db..dd033379a 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUISyncMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUISyncMessages.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "DebugUISyncMessages.h" @@ -7,7 +7,6 @@ #import "OWSTableViewController.h" #import "Signal-Swift.h" #import "ThreadUtil.h" -#import #import #import #import diff --git a/Signal/src/util/Pastelog.h b/Signal/src/util/Pastelog.h index 99356aa0e..0d46da066 100644 --- a/Signal/src/util/Pastelog.h +++ b/Signal/src/util/Pastelog.h @@ -2,14 +2,17 @@ // Copyright (c) 2018 Open Whisper Systems. All rights reserved. // +NS_ASSUME_NONNULL_BEGIN + +typedef void (^SubmitDebugLogsCompletion)(void); + @interface Pastelog : NSObject -typedef void (^DebugLogsUploadedBlock)(NSError *error, NSString *urlString); -typedef void (^DebugLogsSharedBlock)(void); +- (instancetype)init NS_UNAVAILABLE; -+(void)submitLogs; -+ (void)submitLogsWithShareCompletion:(nullable DebugLogsSharedBlock)block; -+ (void)submitLogsWithUploadCompletion:(DebugLogsUploadedBlock)block; -+ (void)submitLogsWithUploadCompletion:(DebugLogsUploadedBlock)block forFileLogger:(DDFileLogger *)fileLogger; ++ (void)submitLogs; ++ (void)submitLogsWithCompletion:(nullable SubmitDebugLogsCompletion)completion; @end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/util/Pastelog.m b/Signal/src/util/Pastelog.m index d6dc5b7cb..9de878707 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,249 +15,413 @@ #import #import -@interface Pastelog () +NS_ASSUME_NONNULL_BEGIN + +typedef void (^UploadDebugLogsSuccess)(NSURL *url); +typedef void (^UploadDebugLogsFailure)(NSString *localizedErrorMessage); + +#pragma mark - + +@class DebugLogUploader; + +typedef void (^DebugLogUploadSuccess)(DebugLogUploader *uploader, NSURL *url); +typedef void (^DebugLogUploadFailure)(DebugLogUploader *uploader, NSError *error); + +@interface DebugLogUploader : NSObject -@property (nonatomic) UIAlertController *loadingAlert; @property (nonatomic) NSMutableData *responseData; -@property (nonatomic) DebugLogsUploadedBlock block; +@property (nonatomic, nullable) DebugLogUploadSuccess success; +@property (nonatomic, nullable) DebugLogUploadFailure failure; @end #pragma mark - -@implementation Pastelog +@implementation DebugLogUploader -+(void)submitLogs { - [self submitLogsWithShareCompletion:nil]; +- (void)dealloc +{ + DDLogVerbose(@"Dealloc: %@", self.logTag); } -+ (void)submitLogsWithShareCompletion:(nullable DebugLogsSharedBlock)shareCompletionParam +- (void)uploadFileWithURL:(NSURL *)fileUrl success:(DebugLogUploadSuccess)success failure:(DebugLogUploadFailure)failure { - DebugLogsSharedBlock shareCompletion = ^{ - if (shareCompletionParam) { - // Wait a moment. If PasteLog opens a URL, it needs a moment to complete. - dispatch_after( - dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), shareCompletionParam); - } - }; + OWSAssert(fileUrl); + OWSAssert(success); + OWSAssert(failure); - [self submitLogsWithUploadCompletion:^(NSError *error, NSString *urlString) { - if (!error) { - UIAlertController *alert = [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_TITLE", @"Title of the debug log alert.") - message:NSLocalizedString( - @"DEBUG_LOG_ALERT_MESSAGE", @"Message of the debug log alert.") - preferredStyle:UIAlertControllerStyleAlert]; - [alert - addAction:[UIAlertAction - actionWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_EMAIL", - @"Label for the 'email debug log' option of the the debug log alert.") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - [Pastelog.sharedManager submitEmail:urlString]; - - shareCompletion(); - }]]; - [alert addAction:[UIAlertAction - actionWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_COPY_LINK", - @"Label for the 'copy link' option of the the debug log alert.") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - UIPasteboard *pb = [UIPasteboard generalPasteboard]; - [pb setString:urlString]; - - shareCompletion(); - }]]; -#ifdef DEBUG - [alert addAction:[UIAlertAction - actionWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_SEND_TO_SELF", - @"Label for the 'send to self' option of the the debug log alert.") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - [Pastelog.sharedManager sendToSelf:urlString]; - }]]; - [alert addAction:[UIAlertAction - actionWithTitle: - NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_SEND_TO_LAST_THREAD", - @"Label for the 'send to last thread' option of the the debug log alert.") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - [Pastelog.sharedManager sendToMostRecentThread:urlString]; - }]]; -#endif - [alert addAction: - [UIAlertAction - actionWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_BUG_REPORT", - @"Label for the 'Open a Bug Report' option of the the debug log alert.") - style:UIAlertActionStyleCancel - handler:^(UIAlertAction *_Nonnull action) { - [Pastelog.sharedManager prepareRedirection:urlString - shareCompletion:shareCompletion]; - }]]; - UIViewController *presentingViewController - = UIApplication.sharedApplication.frontmostViewControllerIgnoringAlerts; - [presentingViewController presentViewController:alert animated:NO completion:nil]; - } else{ - UIAlertView *alertView = - [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"DEBUG_LOG_FAILURE_ALERT_TITLE", - @"Title of the alert indicating the debug log upload failed.") - message:error.localizedDescription - delegate:nil - cancelButtonTitle:@"OK" - otherButtonTitles:nil, nil]; - [alertView show]; - } - }]; -} + self.success = success; + self.failure = failure; + self.responseData = [NSMutableData new]; -+ (void)submitLogsWithUploadCompletion:(DebugLogsUploadedBlock)block -{ - [self submitLogsWithUploadCompletion:block forFileLogger:[[DDFileLogger alloc] init]]; + NSURL *url = [NSURL URLWithString:@"https://filebin.net"]; + + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url + cachePolicy:NSURLRequestReloadIgnoringLocalCacheData + timeoutInterval:30]; + [request setHTTPMethod:@"POST"]; + [request addValue:@"logs.zip" forHTTPHeaderField:@"filename"]; + [request addValue:@"application/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]; } -+ (void)submitLogsWithUploadCompletion:(DebugLogsUploadedBlock)block forFileLogger:(DDFileLogger *)fileLogger +#pragma mark - Delegate Methods + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { + DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); - [self sharedManager].block = block; + [self.responseData appendData:data]; +} - [self sharedManager].loadingAlert = - [UIAlertController alertControllerWithTitle:NSLocalizedString(@"DEBUG_LOG_ACTIVITY_INDICATOR", - @"Message indicating that the debug log is being uploaded.") - message:nil - preferredStyle:UIAlertControllerStyleAlert]; - UIViewController *presentingViewController = UIApplication.sharedApplication.frontmostViewControllerIgnoringAlerts; - [presentingViewController presentViewController:[self sharedManager].loadingAlert animated:NO completion:nil]; +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); - NSArray *logFilePaths = DebugLogger.sharedLogger.allLogFilePaths; + 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; + } - NSMutableDictionary *gistFiles = [NSMutableDictionary new]; + if (![dict isKindOfClass:[NSDictionary class]]) { + DDLogError(@"%@ response (1): %@", self.logTag, dict); + [self failWithError:[NSError errorWithDomain:@"PastelogKit" + code:10003 + userInfo:@{ NSLocalizedDescriptionKey : @"Malformed response (root)." }]]; + return; + } - for (NSString *logFilePath in logFilePaths) { - NSError *error; - NSString *logContents = - [NSString stringWithContentsOfFile:logFilePath encoding:NSUTF8StringEncoding error:&error]; - if (error) { - OWSFail(@"%@ Error loading log file contents: %@", self.logTag, error); + 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; } - gistFiles[logFilePath.lastPathComponent] = @{ - @"content" : logContents, - }; + NSString *_Nullable linkHref = [linkMap objectForKey:@"href"]; + if (![linkHref isKindOfClass:[NSString class]]) { + DDLogError(@"%@ response (linkHref): %@", self.logTag, dict); + continue; + } + urlString = linkHref; + break; } + [self succeedWithUrl:[NSURL URLWithString:urlString]]; +} + +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response +{ + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + + NSInteger statusCode = httpResponse.statusCode; + // We'll accept any 2xx status code. + NSInteger statusCodeClass = statusCode - (statusCode % 100); + if (statusCodeClass != 200) { + DDLogError(@"%@ statusCode: %zd, %zd", self.logTag, statusCode, statusCodeClass); + DDLogError(@"%@ headers: %@", self.logTag, httpResponse.allHeaderFields); + [self failWithError:[NSError errorWithDomain:@"PastelogKit" + code:10001 + userInfo:@{ NSLocalizedDescriptionKey : @"Invalid response code." }]]; + } +} - NSDictionary *gistDict = @{@"description":[self gistDescription], @"files":gistFiles}; +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); - NSData *postData = [NSJSONSerialization dataWithJSONObject:gistDict options:0 error:nil]; + [self failWithError:error]; +} - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:@"https://api.github.com/gists"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:30]; +- (void)failWithError:(NSError *)error +{ + OWSAssert(error); - [[self sharedManager] setResponseData:[NSMutableData data]]; - [[self sharedManager] setBlock:block]; + DDLogError(@"%@ %s %@", self.logTag, __PRETTY_FUNCTION__, error); - [request setHTTPMethod:@"POST"]; - [request setHTTPBody:postData]; + DispatchMainThreadSafe(^{ + // Call the completions exactly once. + if (self.failure) { + self.failure(self, error); + } + self.success = nil; + self.failure = nil; + }); +} - NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:[self sharedManager]]; +- (void)succeedWithUrl:(NSURL *)url +{ + OWSAssert(url); - [connection start]; + DDLogVerbose(@"%@ %s %@", self.logTag, __PRETTY_FUNCTION__, url); + DispatchMainThreadSafe(^{ + // Call the completions exactly once. + if (self.success) { + self.success(self, url); + } + self.success = nil; + self.failure = nil; + }); } -+(Pastelog*)sharedManager { +@end + +#pragma mark - + +@interface Pastelog () + +@property (nonatomic) UIAlertController *loadingAlert; + +@property (nonatomic) DebugLogUploader *currentUploader; + +@end + +#pragma mark - + +@implementation Pastelog + ++ (instancetype)sharedManager +{ static Pastelog *sharedMyManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - sharedMyManager = [[self alloc] init]; + sharedMyManager = [[self alloc] initDefault]; }); return sharedMyManager; } --(instancetype)init { - if (self = [super init]) { - self.responseData = [NSMutableData data]; +- (instancetype)initDefault +{ + self = [super init]; - OWSSingletonAssert(); + if (!self) { + return self; } + + OWSSingletonAssert(); + return self; } -+(NSString*)gistDescription{ - size_t size; - sysctlbyname("hw.machine", NULL, &size, NULL, 0); - char *machine = malloc(size); - sysctlbyname("hw.machine", machine, &size, NULL, 0); - NSString *platform = [NSString stringWithUTF8String:machine]; - free(machine); ++ (void)submitLogs +{ + [self submitLogsWithCompletion:nil]; +} - NSString *gistDesc = [NSString stringWithFormat:@"iPhone Version: %@, iOS Version: %@", platform,[UIDevice currentDevice].systemVersion]; ++ (void)submitLogsWithCompletion:(nullable SubmitDebugLogsCompletion)completionParam +{ + SubmitDebugLogsCompletion completion = ^{ + if (completionParam) { + // Wait a moment. If PasteLog opens a URL, it needs a moment to complete. + dispatch_after( + dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), completionParam); + } + }; - return gistDesc; + [self uploadLogsWithSuccess:^(NSURL *url) { + UIAlertController *alert = [UIAlertController + alertControllerWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_TITLE", @"Title of the debug log alert.") + message:NSLocalizedString(@"DEBUG_LOG_ALERT_MESSAGE", @"Message of the debug log alert.") + preferredStyle:UIAlertControllerStyleAlert]; + [alert addAction:[UIAlertAction + actionWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_EMAIL", + @"Label for the 'email debug log' option of the the debug log alert.") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *_Nonnull action) { + [Pastelog.sharedManager submitEmail:url]; + + completion(); + }]]; + [alert addAction:[UIAlertAction + actionWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_COPY_LINK", + @"Label for the 'copy link' option of the the debug log alert.") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *_Nonnull action) { + UIPasteboard *pb = [UIPasteboard generalPasteboard]; + [pb setString:url.absoluteString]; + + completion(); + }]]; +#ifdef DEBUG + [alert addAction:[UIAlertAction + actionWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_SEND_TO_SELF", + @"Label for the 'send to self' option of the the debug log alert.") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *_Nonnull action) { + [Pastelog.sharedManager sendToSelf:url]; + }]]; + [alert + addAction:[UIAlertAction + actionWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_SEND_TO_LAST_THREAD", + @"Label for the 'send to last thread' option of the the debug log alert.") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *_Nonnull action) { + [Pastelog.sharedManager sendToMostRecentThread:url]; + }]]; +#endif + [alert + addAction:[UIAlertAction + actionWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_BUG_REPORT", + @"Label for the 'Open a Bug Report' option of the the debug log alert.") + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *_Nonnull action) { + [Pastelog.sharedManager prepareRedirection:url completion:completion]; + }]]; + UIViewController *presentingViewController + = UIApplication.sharedApplication.frontmostViewControllerIgnoringAlerts; + [presentingViewController presentViewController:alert animated:NO completion:nil]; + }]; } -#pragma mark Network delegates ++ (void)uploadLogsWithSuccess:(nullable UploadDebugLogsSuccess)success +{ + OWSAssert(success); --(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ - [self.responseData appendData:data]; + [[self sharedManager] uploadLogsWithSuccess:success + failure:^(NSString *localizedErrorMessage) { + [Pastelog showFailureAlertWithMessage:localizedErrorMessage]; + }]; } -- (void)connectionDidFinishLoading:(NSURLConnection *)connection { - [self.loadingAlert - dismissViewControllerAnimated:NO - completion:^{ - NSError *error; - NSDictionary *dict = - [NSJSONSerialization JSONObjectWithData:self.responseData options:0 error:&error]; - if (!error) { - self.block(nil, [dict objectForKey:@"html_url"]); - } else { - DDLogError(@"Error on debug response: %@", error); - self.block(error, nil); - } - }]; - self.loadingAlert = nil; -} +- (void)uploadLogsWithSuccess:(nullable UploadDebugLogsSuccess)successParam failure:(UploadDebugLogsFailure)failureParam +{ + OWSAssert(successParam); + OWSAssert(failureParam); + + // Ensure that we call the completions on the main thread. + UploadDebugLogsSuccess success = ^(NSURL *url) { + if (successParam) { + DispatchMainThreadSafe(^{ + successParam(url); + }); + } + }; + UploadDebugLogsFailure failure = ^(NSString *localizedErrorMessage) { + DispatchMainThreadSafe(^{ + failureParam(localizedErrorMessage); + }); + }; -- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { + // Phase 1. Make a local copy of all of the log files. + NSString *tempDirectory = NSTemporaryDirectory(); + NSString *zipFilePath = + [tempDirectory stringByAppendingPathComponent:[NSUUID.UUID.UUIDString stringByAppendingPathExtension:@"zip"]]; + NSString *zipDirPath = [tempDirectory stringByAppendingPathComponent:NSUUID.UUID.UUIDString]; + [OWSFileSystem ensureDirectoryExists:zipDirPath]; - NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; + NSArray *logFilePaths = DebugLogger.sharedLogger.allLogFilePaths; + if (logFilePaths.count < 1) { + failure(NSLocalizedString(@"DEBUG_LOG_ALERT_NO_LOGS", @"Error indicating that no debug logs could be found.")); + return; + } - if ( [httpResponse statusCode] != 201) { - DDLogError(@"Failed to submit debug log: %@", httpResponse.debugDescription); - [self.loadingAlert - dismissViewControllerAnimated:NO - completion:^{ - [connection cancel]; - self.block([NSError errorWithDomain:@"PastelogKit" code:10001 userInfo:@{}], nil); - }]; - self.loadingAlert = nil; + for (NSString *logFilePath in logFilePaths) { + NSString *copyFilePath = [zipDirPath stringByAppendingPathComponent:logFilePath.lastPathComponent]; + NSError *error; + [[NSFileManager defaultManager] copyItemAtPath:logFilePath toPath:copyFilePath error:&error]; + if (error) { + failure(NSLocalizedString( + @"DEBUG_LOG_ALERT_COULD_NOT_COPY_LOGS", @"Error indicating that the debug logs could not be copied.")); + return; + } + } + + // Phase 2. Zip up the log files. + BOOL zipSuccess = + [SSZipArchive createZipFileAtPath:zipFilePath withContentsOfDirectory:zipDirPath withPassword:nil]; + if (!zipSuccess) { + failure(NSLocalizedString( + @"DEBUG_LOG_ALERT_COULD_NOT_PACKAGE_LOGS", @"Error indicating that the debug logs could not be packaged.")); + return; } + + // Phase 3. Upload the log files. + + __weak Pastelog *weakSelf = self; + self.currentUploader = [DebugLogUploader new]; + [self.currentUploader uploadFileWithURL:[NSURL fileURLWithPath:zipFilePath] + success:^(DebugLogUploader *uploader, NSURL *url) { + if (uploader != weakSelf.currentUploader) { + // Ignore events from obsolete uploaders. + return; + } + success(url); + } + failure:^(DebugLogUploader *uploader, NSError *error) { + if (uploader != weakSelf.currentUploader) { + // Ignore events from obsolete uploaders. + return; + } + failure(NSLocalizedString( + @"DEBUG_LOG_ALERT_ERROR_UPLOADING_LOG", @"Error indicating that a debug log could not be uploaded.")); + }]; } -- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { - [self.loadingAlert dismissViewControllerAnimated:NO - completion:^{ - DDLogError(@"Uploading logs failed with error: %@", error); - self.block(error, nil); - }]; - self.loadingAlert = nil; ++ (void)showFailureAlertWithMessage:(NSString *)message +{ + UIAlertController *alert = [UIAlertController + alertControllerWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_TITLE", + @"Title of the alert shown for failures while uploading debug logs.") + message:message + preferredStyle:UIAlertControllerStyleAlert]; + [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"") + style:UIAlertActionStyleDefault + handler:nil]]; + UIViewController *presentingViewController = UIApplication.sharedApplication.frontmostViewControllerIgnoringAlerts; + [presentingViewController presentViewController:alert animated:NO completion:nil]; } #pragma mark Logs submission -- (void)submitEmail:(NSString*)url { +- (void)submitEmail:(NSURL *)url +{ NSString *emailAddress = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"LOGS_EMAIL"]; NSString *urlString = [NSString stringWithString: [[NSString stringWithFormat:@"mailto:%@?subject=iOS%%20Debug%%20Log&body=", emailAddress] stringByAppendingString:[[NSString stringWithFormat:@"Log URL: %@ \n Tell us about the issue: ", url]stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]]]; - [UIApplication.sharedApplication openURL: [NSURL URLWithString: urlString]]; + [UIApplication.sharedApplication openURL:[NSURL URLWithString:urlString]]; } -- (void)prepareRedirection:(NSString *)url shareCompletion:(DebugLogsSharedBlock)shareCompletion +- (void)prepareRedirection:(NSURL *)url completion:(SubmitDebugLogsCompletion)completion { - OWSAssert(shareCompletion); + OWSAssert(completion); UIPasteboard *pb = [UIPasteboard generalPasteboard]; - [pb setString:url]; + [pb setString:url.absoluteString]; UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"DEBUG_LOG_GITHUB_ISSUE_ALERT_TITLE", @@ -272,13 +437,13 @@ openURL:[NSURL URLWithString:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"LOGS_URL"]]]; - shareCompletion(); + completion(); }]]; UIViewController *presentingViewController = UIApplication.sharedApplication.frontmostViewControllerIgnoringAlerts; [presentingViewController presentViewController:alert animated:NO completion:nil]; } -- (void)sendToSelf:(NSString *)url +- (void)sendToSelf:(NSURL *)url { if (![TSAccountManager isRegistered]) { return; @@ -292,14 +457,14 @@ readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { thread = [TSContactThread getOrCreateThreadWithContactId:recipientId transaction:transaction]; }]; - [ThreadUtil sendMessageWithText:url inThread:thread messageSender:messageSender]; + [ThreadUtil sendMessageWithText:url.absoluteString inThread:thread messageSender:messageSender]; }); // Also copy to pasteboard. - [[UIPasteboard generalPasteboard] setString:url]; + [[UIPasteboard generalPasteboard] setString:url.absoluteString]; } -- (void)sendToMostRecentThread:(NSString *)url +- (void)sendToMostRecentThread:(NSURL *)url { if (![TSAccountManager isRegistered]) { return; @@ -312,11 +477,13 @@ readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { thread = [[transaction ext:TSThreadDatabaseViewExtensionName] firstObjectInGroup:[TSThread collection]]; }]; - [ThreadUtil sendMessageWithText:url inThread:thread messageSender:messageSender]; + [ThreadUtil sendMessageWithText:url.absoluteString inThread:thread messageSender:messageSender]; }); // Also copy to pasteboard. - [[UIPasteboard generalPasteboard] setString:url]; + [[UIPasteboard generalPasteboard] setString:url.absoluteString]; } @end + +NS_ASSUME_NONNULL_END diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 2832ba90c..68ca6f2ce 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -499,12 +499,21 @@ /* The day before today. */ "DATE_YESTERDAY" = "Yesterday"; -/* Message indicating that the debug log is being uploaded. */ -"DEBUG_LOG_ACTIVITY_INDICATOR" = "Sending Debug Log..."; +/* Error indicating that the debug logs could not be copied. */ +"DEBUG_LOG_ALERT_COULD_NOT_COPY_LOGS" = "Could not copy logs."; + +/* Error indicating that the debug logs could not be packaged. */ +"DEBUG_LOG_ALERT_COULD_NOT_PACKAGE_LOGS" = "Could not package logs."; + +/* Error indicating that a debug log could not be uploaded. */ +"DEBUG_LOG_ALERT_ERROR_UPLOADING_LOG" = "Could not upload logs."; /* Message of the debug log alert. */ "DEBUG_LOG_ALERT_MESSAGE" = "What would you like to do with the link to your debug log?"; +/* Error indicating that no debug logs could be found. */ +"DEBUG_LOG_ALERT_NO_LOGS" = "Could not find any logs."; + /* Label for the 'Open a Bug Report' option of the the debug log alert. */ "DEBUG_LOG_ALERT_OPTION_BUG_REPORT" = "Open a Bug Report"; @@ -520,12 +529,10 @@ /* Label for the 'send to self' option of the the debug log alert. */ "DEBUG_LOG_ALERT_OPTION_SEND_TO_SELF" = "Send to Self"; -/* Title of the debug log alert. */ +/* Title of the alert shown for failures while uploading debug logs. + Title of the debug log alert. */ "DEBUG_LOG_ALERT_TITLE" = "One More Step"; -/* Title of the alert indicating the debug log upload failed. */ -"DEBUG_LOG_FAILURE_ALERT_TITLE" = "Failed to Submit Debug Log"; - /* Message of the alert before redirecting to Github Issues. */ "DEBUG_LOG_GITHUB_ISSUE_ALERT_MESSAGE" = "The gist link was copied in your clipboard. You are about to be redirected to the GitHub issue list."; diff --git a/SignalMessaging/profiles/OWSProfileManager.m b/SignalMessaging/profiles/OWSProfileManager.m index 90c280a52..8e74bf85a 100644 --- a/SignalMessaging/profiles/OWSProfileManager.m +++ b/SignalMessaging/profiles/OWSProfileManager.m @@ -7,7 +7,6 @@ #import "NSString+OWS.h" #import "OWSUserProfile.h" #import "UIImage+OWS.h" -#import #import #import #import diff --git a/SignalMessaging/utils/DebugLogger.h b/SignalMessaging/utils/DebugLogger.h index a3ec93bfb..a08037e98 100644 --- a/SignalMessaging/utils/DebugLogger.h +++ b/SignalMessaging/utils/DebugLogger.h @@ -1,9 +1,11 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import +NS_ASSUME_NONNULL_BEGIN + @interface DebugLogger : NSObject + (instancetype)sharedLogger; @@ -19,3 +21,5 @@ - (NSArray *)allLogFilePaths; @end + +NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/utils/DebugLogger.m b/SignalMessaging/utils/DebugLogger.m index cb38f4f8f..bb306982f 100644 --- a/SignalMessaging/utils/DebugLogger.m +++ b/SignalMessaging/utils/DebugLogger.m @@ -11,9 +11,13 @@ #pragma mark Logging - Production logging wants us to write some logs to a file in case we need it for debugging. #import +NS_ASSUME_NONNULL_BEGIN + +const NSUInteger kMaxDebugLogFileSize = 1024 * 1024 * 3; + @interface DebugLogger () -@property (nonatomic) DDFileLogger *fileLogger; +@property (nonatomic, nullable) DDFileLogger *fileLogger; @end @@ -66,9 +70,8 @@ // 24 hour rolling. self.fileLogger.rollingFrequency = kDayInterval; // Keep last 3 days of logs - or last 3 logs (if logs rollover due to max file size). - self.fileLogger.logFileManager.maximumNumberOfLogFiles = 3; - // Raise the max file size per log file to 3 MB. - self.fileLogger.maximumFileSize = 1024 * 1024 * 3; + self.fileLogger.logFileManager.maximumNumberOfLogFiles = 24; + self.fileLogger.maximumFileSize = kMaxDebugLogFileSize; self.fileLogger.logFormatter = [OWSScrubbingLogFormatter new]; [DDLog addLogger:self.fileLogger]; @@ -133,3 +136,5 @@ } @end + +NS_ASSUME_NONNULL_END