diff --git a/Scripts/debug_log_upload.py b/Scripts/debug_log_upload.py new file mode 100755 index 0000000000..0c26aafb4e --- /dev/null +++ b/Scripts/debug_log_upload.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +import sys +import os +import re +import commands +import subprocess +import argparse +import inspect +import urllib2 +import json + +def fail(message): + file_name = __file__ + current_line_no = inspect.stack()[1][2] + current_function_name = inspect.stack()[1][3] + print 'Failure in:', file_name, current_line_no, current_function_name + print message + sys.exit(1) + + +def execute_command(command): + try: + print ' '.join(command) + output = subprocess.check_output(command) + if output: + print output + except subprocess.CalledProcessError as e: + print e.output + sys.exit(1) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Precommit cleanup script.') + parser.add_argument('--file', help='used for starting a new version.') + + args = parser.parse_args() + + params_response = urllib2.urlopen("https://debuglogs.org/").read() + + params = json.loads(params_response) + + upload_url = params['url'] + upload_fields = params['fields'] + upload_key = upload_fields['key'] + upload_key = upload_key + os.path.splitext(args.file)[1] + upload_fields['key'] = upload_key + + download_url = 'https://debuglogs.org/' + upload_key + print 'download_url:', download_url + + curl_command = ['curl', '-v', '-i', '-X', 'POST'] + for field_name in upload_fields: + field_value = upload_fields[field_name] + curl_command.append('-F') + curl_command.append("'%s=%s'" % (field_name, field_value, )) + curl_command.append('-F') + curl_command.append("'file=@%s'" % (args.file,)) + curl_command.append(upload_url) + + # execute_command(curl_command) + print ' '.join(curl_command) + + print 'download_url:', download_url + \ No newline at end of file diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index c1ce8c5553..8caf76b204 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -38,7 +38,7 @@ CFBundleVersion - 2.21.0.0 + 2.21.0.1 ITSAppUsesNonExemptEncryption LOGS_EMAIL diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 4de216e562..254dc626db 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -46,6 +46,7 @@ #import #import #import +#import @import WebRTC; @import Intents; @@ -324,7 +325,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]; @@ -396,6 +397,15 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; if (languageCode.length > 0) { DDLogInfo(@"Language Code: %@", languageCode); } + + 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); + + DDLogInfo(@"iPhone Version: %@", platform); } - (UIViewController *)loadingRootViewController diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index dde2caf405..53f33f1729 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 41ecfa7e98..40eb0cce60 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 51cb890db1..dd033379af 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/ViewControllers/OWSSoundSettingsViewController.m b/Signal/src/ViewControllers/OWSSoundSettingsViewController.m index ce7be75b2d..0246644e1e 100644 --- a/Signal/src/ViewControllers/OWSSoundSettingsViewController.m +++ b/Signal/src/ViewControllers/OWSSoundSettingsViewController.m @@ -74,13 +74,25 @@ NS_ASSUME_NONNULL_BEGIN for (NSNumber *nsValue in allSounds) { OWSSound sound = (OWSSound)nsValue.intValue; OWSTableItem *item; + + NSString *soundLabelText = ^{ + NSString *baseName = [OWSSounds displayNameForSound:sound]; + if (sound == OWSSound_Note) { + NSString *noteStringFormat = NSLocalizedString(@"SETTINGS_AUDIO_DEFAULT_TONE_LABEL_FORMAT", + @"Format string for the default 'Note' sound. Embeds the system {{sound name}}."); + return [NSString stringWithFormat:noteStringFormat, baseName]; + } else { + return [OWSSounds displayNameForSound:sound]; + } + }(); + if (sound == self.currentSound) { - item = [OWSTableItem checkmarkItemWithText:[OWSSounds displayNameForSound:sound] + item = [OWSTableItem checkmarkItemWithText:soundLabelText actionBlock:^{ [weakSelf soundWasSelected:sound]; }]; } else { - item = [OWSTableItem actionItemWithText:[OWSSounds displayNameForSound:sound] + item = [OWSTableItem actionItemWithText:soundLabelText actionBlock:^{ [weakSelf soundWasSelected:sound]; }]; diff --git a/Signal/src/util/Pastelog.h b/Signal/src/util/Pastelog.h index 99356aa0e2..0d46da066e 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 d6dc5b7cb3..17dda81a09 100644 --- a/Signal/src/util/Pastelog.m +++ b/Signal/src/util/Pastelog.m @@ -5,6 +5,10 @@ #import "Pastelog.h" #import "Signal-Swift.h" #import "ThreadUtil.h" +#import "zlib.h" +#import +#import +#import #import #import #import @@ -12,13 +16,227 @@ #import #import #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) NSURL *fileUrl; +@property (nonatomic) NSString *mimeType; +@property (nonatomic, nullable) DebugLogUploadSuccess success; +@property (nonatomic, nullable) DebugLogUploadFailure failure; + +@end + +#pragma mark - + +@implementation DebugLogUploader + +- (void)dealloc +{ + DDLogVerbose(@"Dealloc: %@", self.logTag); +} + +- (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 getUploadParameters]; +} + +- (void)getUploadParameters +{ + __weak DebugLogUploader *weakSelf = self; + + 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; + } + + // Add a file extension to the upload's key. + NSString *fileExtension = weakSelf.fileUrl.lastPathComponent.pathExtension; + uploadKey = [uploadKey stringByAppendingPathExtension:fileExtension]; + NSMutableDictionary *updatedFields = [fields mutableCopy]; + updatedFields[@"key"] = uploadKey; + + [weakSelf uploadFileWithUploadUrl:uploadUrl fields:updatedFields uploadKey:uploadKey]; + } + failure:^(NSURLSessionDataTask *_Nullable task, NSError *error) { + DDLogError(@"%@ failed: %@", weakSelf.logTag, urlString); + [weakSelf failWithError:error]; + }]; +} + +- (void)uploadFileWithUploadUrl:(NSString *)uploadUrl fields:(NSDictionary *)fields uploadKey:(NSString *)uploadKey +{ + 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 = [AFHTTPResponseSerializer 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); + } + } + 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]]; + } + failure:^(NSURLSessionDataTask *_Nullable task, NSError *error) { + DDLogError(@"%@ failed: %@", weakSelf.logTag, uploadUrl); + [weakSelf failWithError:error]; + }]; +} + +- (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." }]]; + } +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + + [self failWithError:error]; +} + +- (void)failWithError:(NSError *)error +{ + OWSAssert(error); + + DDLogError(@"%@ %s %@", self.logTag, __PRETTY_FUNCTION__, error); + + DispatchMainThreadSafe(^{ + // Call the completions exactly once. + if (self.failure) { + self.failure(self, error); + } + self.success = nil; + self.failure = nil; + }); +} + +- (void)succeedWithUrl:(NSURL *)url +{ + OWSAssert(url); + + 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; + }); +} + +@end + +#pragma mark - + +@interface Pastelog () @property (nonatomic) UIAlertController *loadingAlert; -@property (nonatomic) NSMutableData *responseData; -@property (nonatomic) DebugLogsUploadedBlock block; + +@property (nonatomic) DebugLogUploader *currentUploader; @end @@ -26,237 +244,244 @@ @implementation Pastelog -+(void)submitLogs { - [self submitLogsWithShareCompletion:nil]; -} - -+ (void)submitLogsWithShareCompletion:(nullable DebugLogsSharedBlock)shareCompletionParam ++ (instancetype)sharedManager { - 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); - } - }; - - [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]; - } - }]; -} - -+ (void)submitLogsWithUploadCompletion:(DebugLogsUploadedBlock)block -{ - [self submitLogsWithUploadCompletion:block forFileLogger:[[DDFileLogger alloc] init]]; -} - -+ (void)submitLogsWithUploadCompletion:(DebugLogsUploadedBlock)block forFileLogger:(DDFileLogger *)fileLogger -{ - - [self sharedManager].block = block; - - [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]; - - NSArray *logFilePaths = DebugLogger.sharedLogger.allLogFilePaths; - - NSMutableDictionary *gistFiles = [NSMutableDictionary new]; - - 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); - continue; - } - gistFiles[logFilePath.lastPathComponent] = @{ - @"content" : logContents, - }; - } - - NSDictionary *gistDict = @{@"description":[self gistDescription], @"files":gistFiles}; - - NSData *postData = [NSJSONSerialization dataWithJSONObject:gistDict options:0 error:nil]; - - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:@"https://api.github.com/gists"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:30]; - - [[self sharedManager] setResponseData:[NSMutableData data]]; - [[self sharedManager] setBlock:block]; - - [request setHTTPMethod:@"POST"]; - [request setHTTPBody:postData]; - - NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:[self sharedManager]]; - - [connection start]; - -} - -+(Pastelog*)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); - - NSString *gistDesc = [NSString stringWithFormat:@"iPhone Version: %@, iOS Version: %@", platform,[UIDevice currentDevice].systemVersion]; - - return gistDesc; ++ (void)submitLogs +{ + [self submitLogsWithCompletion:nil]; } -#pragma mark Network delegates ++ (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); + } + }; --(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ - [self.responseData appendData:data]; + [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 *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 *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 *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 *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:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + [Pastelog.sharedManager prepareRedirection:url completion:completion]; + }]]; + [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_SHARE", + @"Label for the 'Share' option of the the debug log alert.") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + [AttachmentSharing showShareUIForText:url.absoluteString + completion:completion]; + }]]; + [alert addAction:[OWSAlerts cancelAction]]; + UIViewController *presentingViewController + = UIApplication.sharedApplication.frontmostViewControllerIgnoringAlerts; + [presentingViewController presentViewController:alert animated:NO completion:nil]; + }]; } -- (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)success +{ + OWSAssert(success); + + [[self sharedManager] uploadLogsWithSuccess:success + failure:^(NSString *localizedErrorMessage) { + [Pastelog showFailureAlertWithMessage:localizedErrorMessage]; + }]; } -- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { +- (void)uploadLogsWithSuccess:(nullable UploadDebugLogsSuccess)successParam failure:(UploadDebugLogsFailure)failureParam +{ + OWSAssert(successParam); + OWSAssert(failureParam); - NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; + // 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); + }); + }; - 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; + // Phase 1. Make a local copy of all of the log files. + NSDateFormatter *dateFormatter = [NSDateFormatter new]; + [dateFormatter setLocale:[NSLocale currentLocale]]; + [dateFormatter setDateFormat:@"yyyy.MM.dd hh.mm.ss"]; + NSString *dateString = [dateFormatter stringFromDate:[NSDate new]]; + NSString *logsName = [[dateString stringByAppendingString:@" "] stringByAppendingString:NSUUID.UUID.UUIDString]; + NSString *tempDirectory = NSTemporaryDirectory(); + NSString *zipFilePath = + [tempDirectory stringByAppendingPathComponent:[logsName stringByAppendingPathExtension:@"zip"]]; + NSString *zipDirPath = [tempDirectory stringByAppendingPathComponent:logsName]; + [OWSFileSystem ensureDirectoryExists:zipDirPath]; + [OWSFileSystem protectFileOrFolderAtPath:zipDirPath]; + + 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; } + + 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; + } + [OWSFileSystem protectFileOrFolderAtPath:copyFilePath]; + } + + // Phase 2. Zip up the log files. + BOOL zipSuccess = [SSZipArchive createZipFileAtPath:zipFilePath + withContentsOfDirectory:zipDirPath + keepParentDirectory:YES + compressionLevel:Z_DEFAULT_COMPRESSION + password:nil + AES:NO + progressHandler:nil]; + if (!zipSuccess) { + failure(NSLocalizedString( + @"DEBUG_LOG_ALERT_COULD_NOT_PACKAGE_LOGS", @"Error indicating that the debug logs could not be packaged.")); + return; + } + + [OWSFileSystem protectFileOrFolderAtPath:zipFilePath]; + [OWSFileSystem deleteFile:zipDirPath]; + + // Phase 3. Upload the log files. + + __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. + return; + } + [OWSFileSystem deleteFile:zipFilePath]; + success(url); + } + failure:^(DebugLogUploader *uploader, NSError *error) { + if (uploader != weakSelf.currentUploader) { + // Ignore events from obsolete uploaders. + return; + } + [OWSFileSystem deleteFile:zipFilePath]; + 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]]]; + NSString *body = [NSString stringWithFormat:@"Log URL: %@ \n Tell us about the issue: ", url]; + NSString *escapedBody = + [body stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet]; + NSString *urlString = + [NSString stringWithFormat:@"mailto:%@?subject=iOS%%20Debug%%20Log&body=%@", emailAddress, escapedBody]; - [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", @@ -267,18 +492,18 @@ [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"") style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { + handler:^(UIAlertAction *action) { [UIApplication.sharedApplication 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; @@ -288,35 +513,39 @@ DispatchMainThreadSafe(^{ __block TSThread *thread = nil; - [TSStorageManager.dbReadWriteConnection - readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - thread = [TSContactThread getOrCreateThreadWithContactId:recipientId transaction:transaction]; - }]; - [ThreadUtil sendMessageWithText:url inThread:thread messageSender:messageSender]; + [TSStorageManager.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + thread = [TSContactThread getOrCreateThreadWithContactId:recipientId transaction:transaction]; + }]; + [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; } - OWSMessageSender *messageSender = Environment.current.messageSender; + __block TSThread *thread = nil; + [TSStorageManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + thread = [[transaction ext:TSThreadDatabaseViewExtensionName] firstObjectInGroup:TSInboxGroup]; + }]; DispatchMainThreadSafe(^{ - __block TSThread *thread = nil; - [TSStorageManager.dbReadWriteConnection - readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - thread = [[transaction ext:TSThreadDatabaseViewExtensionName] firstObjectInGroup:[TSThread collection]]; - }]; - [ThreadUtil sendMessageWithText:url inThread:thread messageSender:messageSender]; + if (thread) { + OWSMessageSender *messageSender = Environment.current.messageSender; + [ThreadUtil sendMessageWithText:url.absoluteString inThread:thread messageSender:messageSender]; + } else { + [Pastelog showFailureAlertWithMessage:@"Could not find last thread."]; + } }); // 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 9bf4624a8c..bbd9fdf006 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -502,12 +502,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"; @@ -523,11 +532,12 @@ /* 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. */ -"DEBUG_LOG_ALERT_TITLE" = "One More Step"; +/* Label for the 'Share' option of the the debug log alert. */ +"DEBUG_LOG_ALERT_OPTION_SHARE" = "Share"; -/* Title of the alert indicating the debug log upload failed. */ -"DEBUG_LOG_FAILURE_ALERT_TITLE" = "Failed to Submit Debug Log"; +/* Title of the alert shown for failures while uploading debug logs. + Title of the debug log alert. */ +"DEBUG_LOG_ALERT_TITLE" = "One More Step"; /* 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."; @@ -1563,6 +1573,9 @@ /* No comment provided by engineer. */ "SETTINGS_ADVANCED_TITLE" = "Advanced"; +/* Format string for the default 'Note' sound. Embeds the system {{sound name}}. */ +"SETTINGS_AUDIO_DEFAULT_TONE_LABEL_FORMAT" = "%@ (default)"; + /* A label for the 'add phone number' button in the block list table. */ "SETTINGS_BLOCK_LIST_ADD_BUTTON" = "Add…"; diff --git a/SignalMessaging/attachments/AttachmentSharing.h b/SignalMessaging/attachments/AttachmentSharing.h index 66bf2b833a..712c77157a 100644 --- a/SignalMessaging/attachments/AttachmentSharing.h +++ b/SignalMessaging/attachments/AttachmentSharing.h @@ -2,18 +2,28 @@ // Copyright (c) 2018 Open Whisper Systems. All rights reserved. // +NS_ASSUME_NONNULL_BEGIN + @class TSAttachmentStream; +typedef void (^AttachmentSharingCompletion)(void); + @interface AttachmentSharing : NSObject + (void)showShareUIForAttachment:(TSAttachmentStream *)stream; + (void)showShareUIForURL:(NSURL *)url; ++ (void)showShareUIForURL:(NSURL *)url completion:(nullable AttachmentSharingCompletion)completion; + + (void)showShareUIForText:(NSString *)text; ++ (void)showShareUIForText:(NSString *)text completion:(nullable AttachmentSharingCompletion)completion; + #ifdef DEBUG + (void)showShareUIForUIImage:(UIImage *)image; #endif @end + +NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/attachments/AttachmentSharing.m b/SignalMessaging/attachments/AttachmentSharing.m index 7508a3645f..c648ccd18b 100644 --- a/SignalMessaging/attachments/AttachmentSharing.m +++ b/SignalMessaging/attachments/AttachmentSharing.m @@ -8,6 +8,8 @@ #import #import +NS_ASSUME_NONNULL_BEGIN + @implementation AttachmentSharing + (void)showShareUIForAttachment:(TSAttachmentStream *)stream @@ -18,21 +20,33 @@ } + (void)showShareUIForURL:(NSURL *)url +{ + [self showShareUIForURL:url completion:nil]; +} + ++ (void)showShareUIForURL:(NSURL *)url completion:(nullable AttachmentSharingCompletion)completion { OWSAssert(url); [AttachmentSharing showShareUIForActivityItems:@[ url, - ]]; + ] + completion:completion]; } + (void)showShareUIForText:(NSString *)text +{ + [self showShareUIForText:text completion:nil]; +} + ++ (void)showShareUIForText:(NSString *)text completion:(nullable AttachmentSharingCompletion)completion { OWSAssert(text); [AttachmentSharing showShareUIForActivityItems:@[ text, - ]]; + ] + completion:completion]; } #ifdef DEBUG @@ -42,11 +56,12 @@ [AttachmentSharing showShareUIForActivityItems:@[ image, - ]]; + ] + completion:nil]; } #endif -+ (void)showShareUIForActivityItems:(NSArray *)activityItems ++ (void)showShareUIForActivityItems:(NSArray *)activityItems completion:(nullable AttachmentSharingCompletion)completion { OWSAssert(activityItems); @@ -67,6 +82,10 @@ } else if (completed) { DDLogInfo(@"%@ Did share with activityType: %@", self.logTag, activityType); } + + if (completion) { + DispatchMainThreadSafe(completion); + } }]; UIViewController *fromViewController = CurrentAppContext().frontmostViewController; @@ -84,3 +103,5 @@ } @end + +NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/environment/OWSSounds.m b/SignalMessaging/environment/OWSSounds.m index 54140475b9..bf48527d9c 100644 --- a/SignalMessaging/environment/OWSSounds.m +++ b/SignalMessaging/environment/OWSSounds.m @@ -62,8 +62,9 @@ NSString *const kOWSSoundsStorageGlobalNotificationKey = @"kOWSSoundsStorageGlob + (NSArray *)allNotificationSounds { return @[ - // None should be first. + // None and Note (default) should be first. @(OWSSound_None), + @(OWSSound_Note), @(OWSSound_Aurora), @(OWSSound_Bamboo), @@ -74,7 +75,6 @@ NSString *const kOWSSoundsStorageGlobalNotificationKey = @"kOWSSoundsStorageGlob @(OWSSound_Hello), @(OWSSound_Input), @(OWSSound_Keys), - @(OWSSound_Note), @(OWSSound_Popcorn), @(OWSSound_Pulse), @(OWSSound_Synth), @@ -89,7 +89,7 @@ NSString *const kOWSSoundsStorageGlobalNotificationKey = @"kOWSSoundsStorageGlob OWSFail(@"%@ invalid argument.", self.logTag); return @""; - // Notification Sounds + // Notification Sounds case OWSSound_Aurora: return @"Aurora"; case OWSSound_Bamboo: @@ -117,11 +117,9 @@ NSString *const kOWSSoundsStorageGlobalNotificationKey = @"kOWSSoundsStorageGlob case OWSSound_ClassicNotification: return @"Classic"; - // Ringtone Sounds + // Call Audio case OWSSound_Opening: return @"Opening"; - - // Calls case OWSSound_CallConnecting: return @"Call Connecting"; case OWSSound_CallOutboundRinging: @@ -131,7 +129,7 @@ NSString *const kOWSSoundsStorageGlobalNotificationKey = @"kOWSSoundsStorageGlob case OWSSound_CallFailure: return @"Call Failure"; - // Other + // Other case OWSSound_None: return NSLocalizedString(@"SOUNDS_NONE", @"Label for the 'no sound' option that allows users to disable sounds for notifications, " @@ -246,6 +244,27 @@ NSString *const kOWSSoundsStorageGlobalNotificationKey = @"kOWSSoundsStorageGlob + (void)setGlobalNotificationSound:(OWSSound)sound { + [self.sharedManager setGlobalNotificationSound:sound]; +} + +- (void)setGlobalNotificationSound:(OWSSound)sound +{ + [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { + [self setGlobalNotificationSound:sound transaction:transaction]; + }]; +} + ++ (void)setGlobalNotificationSound:(OWSSound)sound transaction:(YapDatabaseReadWriteTransaction *)transaction +{ + [self.sharedManager setGlobalNotificationSound:sound transaction:transaction]; +} + +- (void)setGlobalNotificationSound:(OWSSound)sound transaction:(YapDatabaseReadWriteTransaction *)transaction +{ + OWSAssert(transaction); + + DDLogInfo(@"%@ Setting global notification sound to: %@", self.logTag, [[self class] displayNameForSound:sound]); + // Fallback push notifications play a sound specified by the server, but we don't want to store this configuration // on the server. Instead, we create a file with the same name as the default to be played when receiving // a fallback notification. @@ -276,16 +295,6 @@ NSString *const kOWSSoundsStorageGlobalNotificationKey = @"kOWSSoundsStorageGlob return; } - OWSSounds *instance = OWSSounds.sharedManager; - [instance.dbConnection setObject:@(sound) - forKey:kOWSSoundsStorageGlobalNotificationKey - inCollection:kOWSSoundsStorageNotificationCollection]; -} - -+ (void)setGlobalNotificationSound:(OWSSound)sound transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert(transaction); - [transaction setObject:@(sound) forKey:kOWSSoundsStorageGlobalNotificationKey inCollection:kOWSSoundsStorageNotificationCollection]; diff --git a/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m b/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m index cfec11736d..8ae0f01eb8 100644 --- a/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m +++ b/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m @@ -65,10 +65,7 @@ NS_ASSUME_NONNULL_BEGIN NSMutableArray *migrationsToRun = [NSMutableArray new]; for (OWSDatabaseMigration *migration in migrations) { - if ([OWSDatabaseMigration fetchObjectWithUniqueID:migration.uniqueId]) { - DDLogDebug(@"%@ Skipping previously run migration: %@", self.logTag, migration); - } else { - DDLogWarn(@"%@ Running migration: %@", self.logTag, migration); + if ([OWSDatabaseMigration fetchObjectWithUniqueID:migration.uniqueId] == nil) { [migrationsToRun addObject:migration]; } } diff --git a/SignalMessaging/profiles/OWSProfileManager.m b/SignalMessaging/profiles/OWSProfileManager.m index b0aecb3a19..76c9a1428b 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 @@ -784,12 +783,9 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640; return nil; } -- (void)downloadAvatarForUserProfile:(OWSUserProfile *)userProfileParameter +- (void)downloadAvatarForUserProfile:(OWSUserProfile *)userProfile { - OWSAssert(userProfileParameter); - - // Make a local copy. - OWSUserProfile *userProfile = [userProfileParameter copy]; + OWSAssert(userProfile); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ if (userProfile.avatarUrlPath.length < 1) { @@ -816,6 +812,8 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640; [self.currentAvatarDownloads addObject:userProfile.recipientId]; } + DDLogVerbose(@"%@ downloading profile avatar: %@", self.logTag, userProfile.uniqueId); + NSString *tempDirectory = NSTemporaryDirectory(); NSString *tempFilePath = [tempDirectory stringByAppendingPathComponent:fileName]; @@ -919,7 +917,6 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640; [userProfile updateWithProfileName:profileName avatarUrlPath:avatarUrlPath - avatarFileName:userProfile.avatarFileName // use existing file name if already downloaded dbConnection:self.dbConnection completion:nil]; @@ -930,18 +927,17 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640; OWSUserProfile *localUserProfile = self.localUserProfile; OWSAssert(localUserProfile); - // Don't clear avatarFileName optimistically. - // * The profile avatar probably isn't out of sync. - // * If the profile avatar is out of sync, it can be synced on next app launch. - // * We don't want to touch local avatar state until we've - // downloaded the latest avatar by downloadAvatarForUserProfile. [localUserProfile updateWithProfileName:profileName avatarUrlPath:avatarUrlPath dbConnection:self.dbConnection completion:nil]; } - if (userProfile.avatarUrlPath.length > 0 && userProfile.avatarFileName.length == 0) { + // Whenever we change avatarUrlPath, OWSUserProfile clears avatarFileName. + // So if avatarUrlPath is set and avatarFileName is not set, we should to + // download this avatar. downloadAvatarForUserProfile will de-bounce + // downloads. + if (userProfile.avatarUrlPath.length > 0 && userProfile.avatarFileName.length < 1) { [self downloadAvatarForUserProfile:userProfile]; } }); diff --git a/SignalMessaging/profiles/OWSUserProfile.h b/SignalMessaging/profiles/OWSUserProfile.h index c58e59e792..35402fff14 100644 --- a/SignalMessaging/profiles/OWSUserProfile.h +++ b/SignalMessaging/profiles/OWSUserProfile.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import diff --git a/SignalMessaging/profiles/OWSUserProfile.m b/SignalMessaging/profiles/OWSUserProfile.m index ef56f4a057..f1942d0878 100644 --- a/SignalMessaging/profiles/OWSUserProfile.m +++ b/SignalMessaging/profiles/OWSUserProfile.m @@ -109,12 +109,14 @@ NSString *const kLocalProfileUniqueId = @"kLocalProfileUniqueId"; BOOL didChange; if (_avatarUrlPath == nil && avatarUrlPath == nil) { didChange = NO; - } else if (_avatarUrlPath != nil && avatarUrlPath != nil) { + } else if (_avatarUrlPath != nil || avatarUrlPath != nil) { didChange = YES; } else { didChange = [_avatarUrlPath isEqualToString:avatarUrlPath]; } + _avatarUrlPath = avatarUrlPath; + if (didChange) { // If the avatarURL changed, the avatarFileName can't be valid. // Clear it. @@ -137,7 +139,9 @@ NSString *const kLocalProfileUniqueId = @"kLocalProfileUniqueId"; dbConnection:(YapDatabaseConnection *)dbConnection completion:(nullable OWSUserProfileCompletion)completion { - NSDictionary *beforeSnapshot = self.dictionaryValue; + // self might be the latest instance, so take a "before" snapshot + // before any changes have been made. + __block NSDictionary *beforeSnapshot = [self.dictionaryValue copy]; changeBlock(self); @@ -146,12 +150,21 @@ NSString *const kLocalProfileUniqueId = @"kLocalProfileUniqueId"; NSString *collection = [[self class] collection]; OWSUserProfile *_Nullable latestInstance = [transaction objectForKey:self.uniqueId inCollection:collection]; if (latestInstance) { + // If self is NOT the latest instance, take a new "before" snapshot + // before updating. + if (self != latestInstance) { + beforeSnapshot = [latestInstance.dictionaryValue copy]; + } + changeBlock(latestInstance); - NSDictionary *afterSnapshot = latestInstance.dictionaryValue; + NSDictionary *afterSnapshot = [latestInstance.dictionaryValue copy]; + if ([beforeSnapshot isEqual:afterSnapshot]) { - DDLogVerbose( - @"%@ Ignoring redundant update in %s: %@", self.logTag, functionName, self.debugDescription); + DDLogVerbose(@"%@ Ignoring redundant update in %s: %@", + self.logTag, + functionName, + self.debugDescription); didChange = NO; } else { [latestInstance saveWithTransaction:transaction]; diff --git a/SignalMessaging/utils/DebugLogger.h b/SignalMessaging/utils/DebugLogger.h index a3ec93bfbf..a08037e981 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 cb38f4f8ff..9d859c6e52 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 @@ -67,8 +71,7 @@ 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.maximumFileSize = kMaxDebugLogFileSize; self.fileLogger.logFormatter = [OWSScrubbingLogFormatter new]; [DDLog addLogger:self.fileLogger]; @@ -133,3 +136,5 @@ } @end + +NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Util/OWSError.h b/SignalServiceKit/src/Util/OWSError.h index ad252ec2c8..03c96a95a0 100644 --- a/SignalServiceKit/src/Util/OWSError.h +++ b/SignalServiceKit/src/Util/OWSError.h @@ -29,7 +29,8 @@ typedef NS_ENUM(NSInteger, OWSErrorCode) { OWSErrorCodeMessageDeletedBeforeSent = 777410, OWSErrorCodeDatabaseConversionFatalError = 777411, OWSErrorCodeMoveFileToSharedDataContainerError = 777412, - OWSErrorCodeRegistrationMissing2FAPIN = 777413 + OWSErrorCodeRegistrationMissing2FAPIN = 777413, + OWSErrorCodeDebugLogUploadFailed = 777414, }; extern NSString *const OWSErrorRecipientIdentifierKey; diff --git a/SignalShareExtension/Info.plist b/SignalShareExtension/Info.plist index 72464dc245..b81d29e0d7 100644 --- a/SignalShareExtension/Info.plist +++ b/SignalShareExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 2.21.0 CFBundleVersion - 2.21.0.0 + 2.21.0.1 ITSAppUsesNonExemptEncryption NSAppTransportSecurity