// // Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import "Pastelog.h" #import "Signal-Swift.h" #import "ThreadUtil.h" #import #import #import #import #import #import #import #import @interface Pastelog () @property (nonatomic) UIAlertController *loadingAlert; @property (nonatomic) NSMutableData *responseData; @property (nonatomic) successBlock block; @end #pragma mark - @implementation Pastelog +(void)submitLogs { [self submitLogsWithCompletion:^(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]; }]]; [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]; }]]; #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]; }]]; #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]; }]]; 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)submitLogsWithCompletion:(successBlock)block { [self submitLogsWithCompletion:(successBlock)block forFileLogger:[[DDFileLogger alloc] init]]; } +(void)submitLogsWithCompletion:(successBlock)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]; }); return sharedMyManager; } -(instancetype)init { if (self = [super init]) { self.responseData = [NSMutableData data]; 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; } #pragma mark Network delegates -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ [self.responseData appendData:data]; } - (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)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; 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; } } - (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; } #pragma mark Logs submission - (void)submitEmail:(NSString*)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]]; } - (void)prepareRedirection:(NSString*)url { UIPasteboard *pb = [UIPasteboard generalPasteboard]; [pb setString:url]; UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"DEBUG_LOG_GITHUB_ISSUE_ALERT_TITLE", @"Title of the alert before redirecting to Github Issues.") message:NSLocalizedString(@"DEBUG_LOG_GITHUB_ISSUE_ALERT_MESSAGE", @"Message of the alert before redirecting to Github Issues.") preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction *_Nonnull action) { [UIApplication.sharedApplication openURL:[NSURL URLWithString:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"LOGS_URL"]]]; }]]; UIViewController *presentingViewController = UIApplication.sharedApplication.frontmostViewControllerIgnoringAlerts; [presentingViewController presentViewController:alert animated:NO completion:nil]; } - (void)sendToSelf:(NSString *)url { if (![TSAccountManager isRegistered]) { return; } NSString *recipientId = [TSAccountManager localNumber]; OWSMessageSender *messageSender = Environment.current.messageSender; DispatchMainThreadSafe(^{ __block TSThread *thread = nil; [[TSStorageManager sharedManager].dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { thread = [TSContactThread getOrCreateThreadWithContactId:recipientId transaction:transaction]; }]; [ThreadUtil sendMessageWithText:url inThread:thread messageSender:messageSender]; }); } @end