diff --git a/.gitignore b/.gitignore index dfd58023..03d3cf0a 100644 --- a/.gitignore +++ b/.gitignore @@ -27,5 +27,7 @@ DerivedData *.xcuserstate mailcore2/unittest/data *.orig + +# Markdown preview README.html CHANGELOG.html diff --git a/AutoPkgr.xcodeproj/project.pbxproj b/AutoPkgr.xcodeproj/project.pbxproj index 06ea1ed3..544992bb 100644 --- a/AutoPkgr.xcodeproj/project.pbxproj +++ b/AutoPkgr.xcodeproj/project.pbxproj @@ -180,6 +180,8 @@ BEC1258B19F04703006696C4 /* LGRepoTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A0BABE8196E560000A136BB /* LGRepoTableViewController.m */; }; BEC1258C19F049A1006696C4 /* LGTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = BE32178D19EDA15400A92E5A /* LGTableView.m */; }; BEC2024B1B600FF100F3BC2A /* LocalizableJSSImporter.strings in Resources */ = {isa = PBXBuildFile; fileRef = BEC202491B600FF100F3BC2A /* LocalizableJSSImporter.strings */; }; + BEC949791B63FDA3005641EB /* LGTwoFactorAuthAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = BEC949781B63FDA3005641EB /* LGTwoFactorAuthAlert.m */; }; + BEC9497A1B63FDA3005641EB /* LGTwoFactorAuthAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = BEC949781B63FDA3005641EB /* LGTwoFactorAuthAlert.m */; }; BECDAB8C1B249C0400544388 /* NSButton+colored.m in Sources */ = {isa = PBXBuildFile; fileRef = BECDAB891B249A2900544388 /* NSButton+colored.m */; }; BECDAB8D1B249C0B00544388 /* NSButton+colored.m in Sources */ = {isa = PBXBuildFile; fileRef = BECDAB891B249A2900544388 /* NSButton+colored.m */; }; BECDAB951B24C29600544388 /* LGMunkiIntegrationView.m in Sources */ = {isa = PBXBuildFile; fileRef = BECDAB931B24C29600544388 /* LGMunkiIntegrationView.m */; }; @@ -488,6 +490,8 @@ BEBF7B151A4894AC00E9967F /* LGVersioner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LGVersioner.h; sourceTree = ""; }; BEBF7B161A4894AC00E9967F /* LGVersioner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LGVersioner.m; sourceTree = ""; }; BEC2024A1B600FF100F3BC2A /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/LocalizableJSSImporter.strings; sourceTree = ""; }; + BEC949771B63FDA3005641EB /* LGTwoFactorAuthAlert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LGTwoFactorAuthAlert.h; sourceTree = ""; }; + BEC949781B63FDA3005641EB /* LGTwoFactorAuthAlert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LGTwoFactorAuthAlert.m; sourceTree = ""; }; BECDAB881B249A2900544388 /* NSButton+colored.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSButton+colored.h"; sourceTree = ""; }; BECDAB891B249A2900544388 /* NSButton+colored.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSButton+colored.m"; sourceTree = ""; }; BECDAB921B24C29600544388 /* LGMunkiIntegrationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LGMunkiIntegrationView.h; sourceTree = ""; }; @@ -1023,6 +1027,8 @@ BE81F2411B22C211007D16C4 /* LGViewWindowController.xib */, BE32178C19EDA15400A92E5A /* LGTableView.h */, BE32178D19EDA15400A92E5A /* LGTableView.m */, + BEC949771B63FDA3005641EB /* LGTwoFactorAuthAlert.h */, + BEC949781B63FDA3005641EB /* LGTwoFactorAuthAlert.m */, BE4DD5451B126E2800854FD8 /* LGSchedulePickerMenu.h */, BE4DD5461B126E2800854FD8 /* LGSchedulePickerMenu.m */, BE5E559F1B1B59D50025CFD3 /* LGTableCellViews.h */, @@ -1545,6 +1551,7 @@ BEC1258C19F049A1006696C4 /* LGTableView.m in Sources */, 6A53625D1988BE59008A949C /* LGTestPort.m in Sources */, BEFC931D1995F0710074C938 /* LGError.m in Sources */, + BEC949791B63FDA3005641EB /* LGTwoFactorAuthAlert.m in Sources */, BEEFE6C21AEBCC7C00882C89 /* LGIntegrationManager.m in Sources */, BEEFE6CE1AEC761F00882C89 /* LGJSSImporterIntegration.m in Sources */, BE05CE8219DAF6320089068B /* LGAutoPkgrAuthorizer.m in Sources */, @@ -1635,6 +1642,7 @@ BE38EA9D1B0CFEA6009FCBEB /* LGScheduleViewController.m in Sources */, BEFC3C8B1A3DF16700C789E9 /* NSString+cleaned.m in Sources */, BEEFE6CB1AEC760900882C89 /* LGAutoPkgIntegration.m in Sources */, + BEC9497A1B63FDA3005641EB /* LGTwoFactorAuthAlert.m in Sources */, BEFC3C8C1A3DF16700C789E9 /* NSTextField+safeStringValue.m in Sources */, BEFC3C8E1A3DF16700C789E9 /* NSArray+filtered.m in Sources */, BEB9AF2C1B41A01900016D98 /* LGSlackNotificationView.m in Sources */, diff --git a/AutoPkgr/Custom Categories/NSData+taskData.m b/AutoPkgr/Custom Categories/NSData+taskData.m index e88d4d8e..1ef0a222 100644 --- a/AutoPkgr/Custom Categories/NSData+taskData.m +++ b/AutoPkgr/Custom Categories/NSData+taskData.m @@ -61,13 +61,13 @@ - (NSDictionary *)taskData_serializedDictionary } - (BOOL)taskData_isInteractive { - NSArray *defaultMatches = @[@"[y/n]:", - @"[YES/NO]:", - @"Password:", - @"Password", + NSArray *defaultMatches = @[@" [y/n]:", + @" [YES/NO]:", + @" Password:", ]; + return [self taskData_isInteractiveWithStrings:defaultMatches]; - } +} - (BOOL)taskData_isInteractiveWithStrings:(NSArray *)interactiveStrings { NSString *message = [[[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; diff --git a/AutoPkgr/Models/AutoPkg Task/LGAutoPkgErrorHandler.h b/AutoPkgr/Models/AutoPkg Task/LGAutoPkgErrorHandler.h index 266a300f..a01ac35c 100644 --- a/AutoPkgr/Models/AutoPkg Task/LGAutoPkgErrorHandler.h +++ b/AutoPkgr/Models/AutoPkg Task/LGAutoPkgErrorHandler.h @@ -30,7 +30,12 @@ typedef NS_ENUM(NSInteger, LGAutoPkgErrorCodes) { kLGAutoPkgErrorNoRecipes = 255, kLGAutoPkgErrorRepoModification = -2, - kLGAutoPkgErrorNeedsRepair = -1 + kLGAutoPkgErrorNeedsRepair = -1, +}; + +typedef NS_ENUM(NSInteger, LGAutoPkgGitHubAPIError) { + kLGAutoPkgErrorGHApi2FAAuthRequired = 1000, + kLGAutoPkgErrorAPITokenNotOnRemote, }; typedef NS_OPTIONS(NSInteger, LGAutoPkgVerb) { @@ -43,17 +48,17 @@ typedef NS_OPTIONS(NSInteger, LGAutoPkgVerb) { kLGAutoPkgInfo = 1 << 4, // repo verbs - kLGAutoPkgRepoAdd = 1 << 10, - kLGAutoPkgRepoDelete = 1 << 11, - kLGAutoPkgRepoUpdate = 1 << 12, - kLGAutoPkgRepoList = 1 << 13, + kLGAutoPkgRepoAdd = 1 << 5, + kLGAutoPkgRepoDelete = 1 << 6, + kLGAutoPkgRepoUpdate = 1 << 7, + kLGAutoPkgRepoList = 1 << 8, // processor verbs - kLGAutoPkgProcessorInfo = 1 << 20, - kLGAutoPkgListProcessors = 1 << 21, + kLGAutoPkgProcessorInfo = 1 << 9, + kLGAutoPkgListProcessors = 1 << 10, // other verbs - kLGAutoPkgVersion = 1 << 30, + kLGAutoPkgVersion = 1 << 11, }; @interface LGAutoPkgErrorHandler : NSObject @@ -64,4 +69,6 @@ typedef NS_OPTIONS(NSInteger, LGAutoPkgVerb) { - (instancetype)initWithVerb:(LGAutoPkgVerb)verb; - (NSError *)errorWithExitCode:(NSInteger)exitCode; ++ (NSError *)errorWithGitHubAPIErrorCode:(LGAutoPkgGitHubAPIError)statusCode; + @end diff --git a/AutoPkgr/Models/AutoPkg Task/LGAutoPkgErrorHandler.m b/AutoPkgr/Models/AutoPkg Task/LGAutoPkgErrorHandler.m index dbb217e1..9d827d81 100644 --- a/AutoPkgr/Models/AutoPkg Task/LGAutoPkgErrorHandler.m +++ b/AutoPkgr/Models/AutoPkg Task/LGAutoPkgErrorHandler.m @@ -21,7 +21,7 @@ #import "LGAutoPkgErrorHandler.h" #import "LGLogger.h" -NSString * LGAutoPkgLocalizedString(NSString *key, NSString *comment) +NSString *LGAutoPkgLocalizedString(NSString *key, NSString *comment) { return [[NSBundle mainBundle] localizedStringForKey:key value:key @@ -34,41 +34,62 @@ switch (verb) { case kLGAutoPkgRun: - message = NSLocalizedStringFromTable(@"Error running recipes", @"LocalizableAutoPkg", nil); + message = NSLocalizedStringFromTable(@"Error running recipes", @"LocalizableAutoPkg", nil); break; case kLGAutoPkgListRecipes: - message = NSLocalizedStringFromTable(@"Error encountered listing recipes", @"LocalizableAutoPkg", nil); + message = NSLocalizedStringFromTable(@"Error encountered listing recipes", @"LocalizableAutoPkg", nil); break; case kLGAutoPkgMakeOverride: - message = NSLocalizedStringFromTable(@"Error creating recipe override file", @"LocalizableAutoPkg", nil); + message = NSLocalizedStringFromTable(@"Error creating recipe override file", @"LocalizableAutoPkg", nil); break; case kLGAutoPkgSearch: - message = NSLocalizedStringFromTable(@"Error encountered searching for recipe", @"LocalizableAutoPkg", nil); + message = NSLocalizedStringFromTable(@"Error encountered searching for recipe", @"LocalizableAutoPkg", nil); break; case kLGAutoPkgRepoAdd: - message = NSLocalizedStringFromTable(@"Error adding repo", @"LocalizableAutoPkg", nil); + message = NSLocalizedStringFromTable(@"Error adding repo", @"LocalizableAutoPkg", nil); break; case kLGAutoPkgRepoDelete: - message = NSLocalizedStringFromTable(@"Error removing repo", @"LocalizableAutoPkg", nil); + message = NSLocalizedStringFromTable(@"Error removing repo", @"LocalizableAutoPkg", nil); break; case kLGAutoPkgRepoUpdate: - message = NSLocalizedStringFromTable(@"Error updating repo", @"LocalizableAutoPkg", nil); + message = NSLocalizedStringFromTable(@"Error updating repo", @"LocalizableAutoPkg", nil); break; case kLGAutoPkgRepoList: - message = NSLocalizedStringFromTable(@"Error encountered listing repos", @"LocalizableAutoPkg", nil); + message = NSLocalizedStringFromTable(@"Error encountered listing repos", @"LocalizableAutoPkg", nil); break; case kLGAutoPkgVersion: - message = NSLocalizedStringFromTable(@"Error getting AutoPkg version", @"LocalizableAutoPkg", nil); + message = NSLocalizedStringFromTable(@"Error getting AutoPkg version", @"LocalizableAutoPkg", nil); break; case kLGAutoPkgUndefinedVerb: default: - message = NSLocalizedStringFromTable(@"AutoPkgr encountered an error", @"LocalizableAutoPkg", nil); + message = NSLocalizedStringFromTable(@"AutoPkgr encountered an error", @"LocalizableAutoPkg", nil); break; } return message; } +static NSDictionary *errorFromAutoPkgGitHubAPICode(LGAutoPkgGitHubAPIError code) +{ + NSDictionary *userInfo = nil; + switch (code) { + case kLGAutoPkgErrorGHApi2FAAuthRequired: { + userInfo = @{ NSLocalizedDescriptionKey : NSLocalizedStringFromTable(@"Two factor authentication is required", @"LocalizableAutoPkg", nil), + NSLocalizedRecoverySuggestionErrorKey : NSLocalizedStringFromTable(@"Your GitHub account has two factor authentication enabled. If you did not recieve a code on your mobile device, check your account's security settings and confirm the information there is correct.", @"LocalizableAutoPkg", nil) }; + break; + } + case kLGAutoPkgErrorAPITokenNotOnRemote: { + userInfo = @{ NSLocalizedDescriptionKey : NSLocalizedStringFromTable(@"API Token not found.", @"LocalizableAutoPkg", nil), + NSLocalizedRecoverySuggestionErrorKey : NSLocalizedStringFromTable(@"A GitHub API token matching the local one was not found on the remote. You may need to remove it manually. If you've just added the token, you may need to wait a minute to delete it.", @"LocalizableAutoPkg", nil) }; + } break; + default: { + userInfo = @{ NSLocalizedDescriptionKey : NSLocalizedStringFromTable(@"There was a problem communicating with GitHub", @"LocalizableAutoPkg", nil) }; + break; + } + } + return userInfo; +} + NSString *maskPasswordInString(NSString *string) { NSError *error; @@ -117,16 +138,16 @@ - (instancetype)initWithVerb:(LGAutoPkgVerb)verb _pipe = pipe; [pipe.fileHandleForReading setReadabilityHandler:^(NSFileHandle *fh) { - NSData *data = fh.availableData; - if (data) { - NSString *str = [[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + NSData *data = fh.availableData; + if (data) { + NSString *str = [[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - if (!_errorStrings) { - _errorStrings = [[NSMutableOrderedSet alloc] init]; - } - - [_errorStrings addObject:str]; - } + if (!_errorStrings) { + _errorStrings = [[NSMutableOrderedSet alloc] init]; + } + + [_errorStrings addObject:str]; + } }]; } return self; @@ -197,7 +218,7 @@ - (NSError *)errorWithExitCode:(NSInteger)exitCode } // autopkg run exits 255 if no recipe specified else if (_verb == kLGAutoPkgRun && exitCode == kLGAutoPkgErrorNoRecipes) { - errorDetails = LGAutoPkgLocalizedString(@"No recipes specified.", nil) ; + errorDetails = LGAutoPkgLocalizedString(@"No recipes specified.", nil); } } @@ -218,4 +239,13 @@ - (NSError *)errorWithExitCode:(NSInteger)exitCode return error; } ++ (NSError *)errorWithGitHubAPIErrorCode:(LGAutoPkgGitHubAPIError)statusCode +{ + NSDictionary *userInfo = errorFromAutoPkgGitHubAPICode(statusCode); + NSError *error = [NSError errorWithDomain:[[NSBundle mainBundle] bundleIdentifier] ?: @"autopkg" + code:statusCode + userInfo:userInfo]; + return error; +} + @end diff --git a/AutoPkgr/Models/AutoPkg Task/LGAutoPkgRecipe.m b/AutoPkgr/Models/AutoPkg Task/LGAutoPkgRecipe.m index e28c4387..2d16932b 100644 --- a/AutoPkgr/Models/AutoPkg Task/LGAutoPkgRecipe.m +++ b/AutoPkgr/Models/AutoPkg Task/LGAutoPkgRecipe.m @@ -79,8 +79,9 @@ - (instancetype)initWithRecipeFile:(NSURL *)recipeFile isOverride:(BOOL)isOverri _enabledInitialized = -1; [_identifierURLStore setObject:_recipeFileURL forKey:_Identifier]; + return self; } - return self; + return nil; } - (NSString *)Description @@ -416,7 +417,7 @@ + (BOOL)removeRecipeFromRecipeList:(NSString *)recipe NSString *recipe_list = [recipes.array componentsJoinedByString:@"\n"]; if (![recipe_list writeToFile:[self defaultRecipeList] atomically:YES encoding:NSUTF8StringEncoding error:&error]) { - NSLog(@"%@", error); + NSLog(@"Error writing file: %@", error.localizedDescription); return NO; } return YES; diff --git a/AutoPkgr/Models/AutoPkg Task/LGAutoPkgRepo.h b/AutoPkgr/Models/AutoPkg Task/LGAutoPkgRepo.h index 9d04d3c1..d52f4314 100644 --- a/AutoPkgr/Models/AutoPkg Task/LGAutoPkgRepo.h +++ b/AutoPkgr/Models/AutoPkg Task/LGAutoPkgRepo.h @@ -46,6 +46,15 @@ typedef NS_ENUM(OSStatus, LGAutoPkgRepoStatus) { @property (assign, nonatomic, readonly) LGAutoPkgRepoStatus status; @property (copy) void (^statusChangeBlock)(LGAutoPkgRepoStatus); +/** + * Initialize a repo with a clone url + * + * @param cloneURL clone URL + * + * @return initialized AutoPkgRepo Object + */ +- (instancetype)initWithCloneURL:(NSString *)cloneURL; + /** * Check if there are updates available for the repo. * diff --git a/AutoPkgr/Models/AutoPkg Task/LGAutoPkgRepo.m b/AutoPkgr/Models/AutoPkg Task/LGAutoPkgRepo.m index 5b244dcb..47342da2 100644 --- a/AutoPkgr/Models/AutoPkg Task/LGAutoPkgRepo.m +++ b/AutoPkgr/Models/AutoPkg Task/LGAutoPkgRepo.m @@ -76,9 +76,19 @@ - (instancetype)initWithCloneURL:(NSString *)cloneURL } #pragma mark - Accessors +- (NSString *)description { + return self.cloneURL.absoluteString; +} + - (BOOL)isInstalled { - NSPredicate *repoPredicate = [NSPredicate predicateWithFormat:@"%K CONTAINS[cd] %@", kLGAutoPkgRepoURLKey, self.cloneURL.absoluteString]; + NSMutableString *repoURL = self.cloneURL.absoluteString.mutableCopy; + if ([repoURL.pathExtension isEqualToString:@"git"]) { + [repoURL deleteCharactersInRange:NSMakeRange(repoURL.length - 4, 4)]; + } + + NSPredicate *repoPredicate = [NSPredicate predicateWithFormat:@"%K CONTAINS[cd] %@", kLGAutoPkgRepoURLKey, repoURL]; + return [[_activeRepos filteredArrayUsingPredicate:repoPredicate] count]; } @@ -140,13 +150,18 @@ - (void)updateRepo:(void (^)(NSError *))reply - (void)install:(void (^)(NSError *))reply { - [LGAutoPkgTask repoAdd:_cloneURL.absoluteString reply:^(NSError *error) { - _activeRepos = [LGAutoPkgTask repoList]; - if (!error) { - [self statusDidChange:kLGAutoPkgRepoUpToDate]; - } - reply(error); - }]; + _activeRepos = [LGAutoPkgTask repoList]; + if (!self.isInstalled) { + [LGAutoPkgTask repoAdd:_cloneURL.absoluteString reply:^(NSError *error) { + _activeRepos = [LGAutoPkgTask repoList]; + if (!error) { + [self statusDidChange:kLGAutoPkgRepoUpToDate]; + } + reply(error); + }]; + } else { + reply(nil); + } } - (void)remove:(void (^)(NSError *))reply @@ -245,19 +260,33 @@ + (void)commonRepos:(void (^)(NSArray *))reply { void (^constructCommonRepos)() = ^() { _activeRepos = [LGAutoPkgTask repoList]; - NSMutableArray *commonRepos = [_popularRepos mutableCopy]; + NSMutableArray *commonRepos = [NSMutableArray arrayWithArray:_popularRepos]; [_activeRepos enumerateObjectsUsingBlock:^(NSDictionary *activeRepo, NSUInteger idx, BOOL *stop) { + NSString *repoURL = activeRepo[kLGAutoPkgRepoURLKey]; + NSMutableString *normalizedRepo = repoURL.mutableCopy; + + if (![normalizedRepo.pathExtension isEqualToString:@"git"]) { + // Using -stringByAppendingPathComponent: on a url here + // results in the scheme getting mangled from https://xxx to https:/xxx + // so just use -stringByAppendingString: string. + [normalizedRepo appendString:@".git"]; + } NSPredicate *pred = [NSPredicate predicateWithFormat:@"%K.absoluteString == %@", NSStringFromSelector(@selector(cloneURL)), - activeRepo[kLGAutoPkgRepoURLKey]]; + normalizedRepo]; + + NSArray *matches = [_popularRepos filteredArrayUsingPredicate:pred]; - if ([_popularRepos filteredArrayUsingPredicate:pred].count == 0) { + if (matches.count == 0) { LGAutoPkgRepo *repo = nil; if ((repo = [[self alloc] initWithAutoPkgDictionary:activeRepo])) { [commonRepos addObject:repo]; } + } if (matches.count == 1 && ![repoURL isEqualToString:normalizedRepo]) { + LGAutoPkgRepo *repo = matches.firstObject; + repo->_cloneURL = [NSURL URLWithString:repoURL]; } }]; diff --git a/AutoPkgr/Models/AutoPkg Task/LGAutoPkgResultHandler.m b/AutoPkgr/Models/AutoPkg Task/LGAutoPkgResultHandler.m index e1c394da..ceafedfd 100644 --- a/AutoPkgr/Models/AutoPkg Task/LGAutoPkgResultHandler.m +++ b/AutoPkgr/Models/AutoPkg Task/LGAutoPkgResultHandler.m @@ -114,10 +114,7 @@ - (id)results __block NSMutableArray *searchResults; - NSMutableCharacterSet *repoCharacters = [NSMutableCharacterSet alphanumericCharacterSet]; - [repoCharacters formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]]; - - NSCharacterSet *skippedCharacters = [NSCharacterSet whitespaceCharacterSet]; + NSCharacterSet *whiteSpace = [NSCharacterSet whitespaceAndNewlineCharacterSet]; NSPredicate *skipLinePredicate = [NSPredicate predicateWithFormat:@"SELF BEGINSWITH 'To add' \ or SELF BEGINSWITH '----' \ @@ -125,17 +122,12 @@ - (id)results [self.dataString enumerateLinesUsingBlock:^(NSString *line, BOOL *stop) { if (![skipLinePredicate evaluateWithObject:line ]) { - NSScanner *scanner = [NSScanner scannerWithString:line]; - [scanner setCharactersToBeSkipped:skippedCharacters]; - - NSString *recipe, *repo, *path; - - [scanner scanCharactersFromSet:repoCharacters intoString:&recipe]; - [scanner scanCharactersFromSet:repoCharacters intoString:&repo]; - [scanner scanCharactersFromSet:repoCharacters intoString:&path]; - + NSArray *array = [line componentsSeparatedByString:@" "].filtered_noEmptyStrings; + if (array.count == 3) { + NSString *recipe = [array[0] stringByTrimmingCharactersInSet:whiteSpace]; + NSString *repo = [array[1] stringByTrimmingCharactersInSet:whiteSpace]; + NSString *path = [array[2] stringByTrimmingCharactersInSet:whiteSpace]; - if (recipe && repo && path) { if (!searchResults) { searchResults = [[NSMutableArray alloc] init]; } diff --git a/AutoPkgr/Models/AutoPkg Task/LGAutoPkgTask.m b/AutoPkgr/Models/AutoPkg Task/LGAutoPkgTask.m index e98aad57..de5b1e69 100644 --- a/AutoPkgr/Models/AutoPkg Task/LGAutoPkgTask.m +++ b/AutoPkgr/Models/AutoPkg Task/LGAutoPkgTask.m @@ -23,6 +23,8 @@ #import "LGAutoPkgResultHandler.h" #import "LGHostInfo.h" #import "LGVersioner.h" +#import "LGTwoFactorAuthAlert.h" + #import "BSDProcessInfo.h" #import "NSData+taskData.h" @@ -56,7 +58,7 @@ static NSString *const AUTOPKG_0_4_0 = @"0.4.0"; static NSString *const AUTOPKG_0_4_1 = @"0.4.1"; static NSString *const AUTOPKG_0_4_2 = @"0.4.2"; -static NSString *const AUTOPKG_0_4_3 = @"0.4.3"; +static NSString *const AUTOPKG_0_5_0 = @"0.5.0"; // Autopkg Task Result keys NSString *const kLGAutoPkgRecipeNameKey = @"Name"; @@ -165,8 +167,8 @@ - (void)runRecipes:(NSArray *)recipes } - (void)runRecipes:(NSArray *)recipes - withInteraction:(BOOL)withInteraction - reply:(void (^)(NSDictionary *, NSError *))reply + withInteraction:(BOOL)withInteraction + reply:(void (^)(NSDictionary *, NSError *))reply { LGAutoPkgTask *task = [LGAutoPkgTask runRecipesTask:recipes]; task.replyReportBlock = reply; @@ -312,7 +314,7 @@ - (void)main __weak typeof(self) weakSelf = self; [self.task setTerminationHandler:^(NSTask *task) { - [weakSelf didCompleteTaskExecution]; + [weakSelf didCompleteTaskExecution]; }]; // Since NSTask can raise for unexpected reasons, @@ -320,14 +322,13 @@ - (void)main @try { [self.task launch]; } - @catch (NSException *exception) - { + @catch (NSException *exception) { NSString *message = LGAutoPkgLocalizedString(@"A fatal error occurred when trying to run AutoPkg", nil); - NSString *suggestion = LGAutoPkgLocalizedString( @"If you repeatedly see this message please report it. The full scope of the error is in the system.log, make sure to include that in the report", nil); + NSString *suggestion = LGAutoPkgLocalizedString(@"If you repeatedly see this message please report it. The full scope of the error is in the system.log, make sure to include that in the report", nil); NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : message, - NSLocalizedRecoverySuggestionErrorKey : suggestion}; + NSLocalizedRecoverySuggestionErrorKey : suggestion }; NSLog(@"[AutoPkgr EXCEPTION] %@ %@", exception.reason, exception.userInfo); @@ -382,7 +383,7 @@ - (void)didCompleteTaskExecution if (_verb & (kLGAutoPkgRepoAdd | kLGAutoPkgRepoDelete | kLGAutoPkgRepoUpdate)) { // Post a notification for objects watching for modified repos. dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:kLGNotificationReposModified object:nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:kLGNotificationReposModified object:nil]; }); } @@ -519,63 +520,63 @@ - (void)configureFileHandles BOOL verbose = [[NSUserDefaults standardUserDefaults] boolForKey:@"verboseAutoPkgRun"]; [[standardOutput fileHandleForReading] setReadabilityHandler:^(NSFileHandle *handle) { - NSData *data = handle.availableData; - - if (data.length) { - NSString *message = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; - - if (isInteractive && data.taskData_isInteractive) { - DevLog(@"Prompting for interaction: %@", message); - return [self interactiveAlertWithMessage:message]; - } - - [_versioner parseString:message]; - if ([progressPredicate evaluateWithObject:message]) { - NSString *fullMessage; - if (_verb == kLGAutoPkgRepoUpdate) { - fullMessage = [NSString stringWithFormat:@"Updating %@", [message lastPathComponent]]; - } else { - int cntStr = (int)round(count) + 1; - int totStr = (int)round(total); - fullMessage = [[NSString stringWithFormat:@"(%d/%d) %@", cntStr, totStr, message] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; - } - - double progress = ((count/total) * 100); - - LGAutoPkgTaskResponseObject *response = [[LGAutoPkgTaskResponseObject alloc] init]; - response.progressMessage = fullMessage; - response.progress = progress; - count++; - - [(NSObject *)_taskStatusDelegate performSelectorOnMainThread:@selector(didReceiveStatusUpdate:) withObject:response waitUntilDone:NO]; - - // If verboseAutoPkgRun is not enabled, log the limited message here. - if (!verbose) { - NSLog(@"%@",message); - } - } - // If verboseAutoPkgRun is enabled, log everything generated by autopkg run -v. - if (verbose) { - NSLog(@"%@",message); - } - } + NSData *data = handle.availableData; + + if (data.length) { + NSString *message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + + if (isInteractive && data.taskData_isInteractive) { + DevLog(@"Prompting for interaction: %@", message); + return [self interactiveAlertWithMessage:message]; + } + + [_versioner parseString:message]; + if ([progressPredicate evaluateWithObject:message]) { + NSString *fullMessage; + if (_verb == kLGAutoPkgRepoUpdate) { + fullMessage = [NSString stringWithFormat:@"Updating %@", [message lastPathComponent]]; + } else { + int cntStr = (int)round(count) + 1; + int totStr = (int)round(total); + fullMessage = [[NSString stringWithFormat:@"(%d/%d) %@", cntStr, totStr, message] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; + } + + double progress = ((count / total) * 100); + + LGAutoPkgTaskResponseObject *response = [[LGAutoPkgTaskResponseObject alloc] init]; + response.progressMessage = fullMessage; + response.progress = progress; + count++; + + [(NSObject *)_taskStatusDelegate performSelectorOnMainThread:@selector(didReceiveStatusUpdate:) withObject:response waitUntilDone:NO]; + + // If verboseAutoPkgRun is not enabled, log the limited message here. + if (!verbose) { + NSLog(@"%@", message); + } + } + // If verboseAutoPkgRun is enabled, log everything generated by autopkg run -v. + if (verbose) { + NSLog(@"%@", message); + } + } }]; } } else { // In order to prevent maxing out the stdout buffer collect the data progressively // even thought the data returned is usually small. [[standardOutput fileHandleForReading] setReadabilityHandler:^(NSFileHandle *fh) { - NSData *data = [fh availableData]; - if (data.length) { - if (isInteractive && data.taskData_isInteractive) { - return [self interactiveAlertWithMessage:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]]; - } - - if (!_standardOutData) { - _standardOutData = [[NSMutableData alloc] init ]; - } - [_standardOutData appendData:data]; - } + NSData *data = [fh availableData]; + if (data.length) { + if (isInteractive && data.taskData_isInteractive) { + return [self interactiveAlertWithMessage:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]]; + } + + if (!_standardOutData) { + _standardOutData = [[NSMutableData alloc] init]; + } + [_standardOutData appendData:data]; + } }]; } } @@ -770,7 +771,19 @@ - (BOOL)isNetworkOperation - (BOOL)isInteractiveOperation { - return (_verb & (kLGAutoPkgRun | kLGAutoPkgInfo)) && [_version version_isGreaterThanOrEqualTo:AUTOPKG_0_4_3]; + BOOL isInteractiveOperation = NO; + if([_version version_isGreaterThanOrEqualTo:AUTOPKG_0_5_0]){ + if (_verb == kLGAutoPkgInfo) { + isInteractiveOperation = YES; + } else if (_verb == kLGAutoPkgRun){ + // As of AutoPkg 0.5.0 AutoPkg will only "make_suggestions" + // when not running with the --recipe-list argument. + if (![_arguments containsObject:@"--recipe-list"]) { + isInteractiveOperation = YES; + } + } + } + return isInteractiveOperation; } - (NSInteger)recipeListCount @@ -804,26 +817,27 @@ - (void)interactiveAlertWithMessage:(NSString *)message { /* Eventually there may be more ways to interact, for now it's only to search github for a recipe's repo */ [[NSOperationQueue mainQueue] addOperationWithBlock:^{ - NSString *title = LGAutoPkgLocalizedString(@"Could not find the parent recipe" - , nil); - - NSString *cleanedMessage = [message stringByReplacingOccurrencesOfString:@"[y/n]:" - withString:@""]; - NSAlert *alert = [NSAlert alertWithMessageText:title - defaultButton:@"Yes" - alternateButton:@"No" - otherButton:nil - informativeTextWithFormat:@"%@", cleanedMessage]; - - NSString *results; - // Don't forget the newline char! - if ([alert runModal] == NSAlertDefaultReturn) { - results = @"y\n"; - } else { - results = @"n\n"; + NSString *title = LGAutoPkgLocalizedString(@"Could not find the parent recipe", nil); + + NSString *cleanedMessage = [message stringByReplacingOccurrencesOfString:@"[y/n]:" + withString:@""]; + NSAlert *alert = [NSAlert alertWithMessageText:title + defaultButton:@"Yes" + alternateButton:@"No" + otherButton:nil + informativeTextWithFormat:@"%@", cleanedMessage]; + + NSString *results; + // Don't forget the newline char! + if ([alert runModal] == NSAlertDefaultReturn) { + results = @"y\n"; + } else { + results = @"n\n"; + } + + if (self.task.isRunning) { + [[self.task.standardInput fileHandleForWriting] writeData:[results dataUsingEncoding:NSUTF8StringEncoding]]; } - - [[self.task.standardInput fileHandleForWriting] writeData:[results dataUsingEncoding:NSUTF8StringEncoding]]; }]; } @@ -838,7 +852,7 @@ + (void)runRecipes:(NSArray *)recipes __weak typeof(task) weakTask = task; [task launchInBackground:^(NSError *error) { - reply(weakTask.report,error); + reply(weakTask.report, error); }]; } @@ -851,7 +865,7 @@ + (void)runRecipeList:(NSString *)recipeList __weak typeof(task) weakTask = task; [task launchInBackground:^(NSError *error) { - reply(weakTask.report,error); + reply(weakTask.report, error); }]; } @@ -860,11 +874,11 @@ + (void)search:(NSString *)recipe reply:(void (^)(NSArray *results, NSError *err LGAutoPkgTask *task = [LGAutoPkgTask searchTask:recipe]; __weak typeof(task) weakTask = task; [task launchInBackground:^(NSError *error) { - NSArray *results; - if (!error) { - results = [weakTask results]; - } - reply(results, error); + NSArray *results; + if (!error) { + results = [weakTask results]; + } + reply(results, error); }]; } @@ -884,17 +898,17 @@ + (void)makeOverride:(NSString *)recipe name:(NSString *)name reply:(void (^)(NS task.arguments = args; __weak typeof(task) weakTask = task; [task launchInBackground:^(NSError *error) { - typeof(task) strongTask = weakTask; - NSMutableString *path = nil; - if(!error){ - path = [strongTask.standardOutString.trimmed mutableCopy]; - [path deleteCharactersInRange:[path rangeOfString:@"Override file saved to "]]; - if ((path.length > 2) && ([path characterAtIndex:path.length-1] == '.')) { - [path deleteCharactersInRange:NSMakeRange(path.length-1, 1)]; - } - } - - reply(path, error); + typeof(task) strongTask = weakTask; + NSMutableString *path = nil; + if (!error) { + path = [strongTask.standardOutString.trimmed mutableCopy]; + [path deleteCharactersInRange:[path rangeOfString:@"Override file saved to "]]; + if ((path.length > 2) && ([path characterAtIndex:path.length - 1] == '.')) { + [path deleteCharactersInRange:NSMakeRange(path.length - 1, 1)]; + } + } + + reply(path, error); }]; } @@ -913,8 +927,8 @@ + (void)info:(NSString *)recipe reply:(void (^)(NSString *info, NSError *error)) LGAutoPkgTask *task = [[LGAutoPkgTask alloc] initWithArguments:@[ @"info", recipe ]]; __weak typeof(task) weakTask = task; [task launchInBackground:^(NSError *error) { - typeof(task) strongTask = weakTask; - reply(strongTask.standardOutString, error); + typeof(task) strongTask = weakTask; + reply(strongTask.standardOutString, error); }]; } @@ -926,11 +940,11 @@ + (void)repoAdd:(NSString *)repo reply:(void (^)(NSError *))reply LGAutoPkgTask *task = [[LGAutoPkgTask alloc] init]; // Make sure everything that comes in has the .git extension. if (![repo.pathExtension isEqualToString:@"git"]) { - repo = [repo stringByAppendingPathExtension:@"git"]; + repo = [repo stringByAppendingString:@".git"]; } task.arguments = @[ @"repo-add", repo ]; [task launchInBackground:^(NSError *error) { - reply(error); + reply(error); }]; } @@ -940,7 +954,7 @@ + (void)repoRemove:(NSString *)repo reply:(void (^)(NSError *))reply LGAutoPkgTask *task = [[LGAutoPkgTask alloc] init]; task.arguments = @[ @"repo-delete", repo ]; [task launchInBackground:^(NSError *error) { - reply(error); + reply(error); }]; } @@ -950,7 +964,7 @@ + (void)repoUpdate:(void (^)(NSString *, double taskProgress))progress LGAutoPkgTask *task = [[LGAutoPkgTask alloc] initWithArguments:@[ @"repo-update", @"all" ]]; task.progressUpdateBlock = progress; [task launchInBackground:^(NSError *error) { - reply(error); + reply(error); }]; } @@ -979,27 +993,7 @@ + (NSString *)processorInfo:(NSString *)processor } #pragma mark - Other -+ (NSString *)version -{ - NSString *version; - - NSTask *task = [[NSTask alloc] init]; - - task = task; - task.launchPath = @"/usr/bin/python"; - task.arguments = @[ autopkg(), @"version" ]; - task.standardOutput = [NSPipe pipe]; - [task launch]; - [task waitUntilExit]; - - NSData *data = [[task.standardOutput fileHandleForReading] availableData]; - if (data.length) { - version = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - } - - return [version stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] ?: @"0.0.0"; -} - +#pragma mark-- GitHub API token -- + (NSString *)apiToken { NSString *tokenFile = nil; @@ -1018,45 +1012,44 @@ + (BOOL)apiTokenFileExists:(NSString *__autoreleasing *)file } + (void)generateGitHubAPIToken:(NSString *)username password:(NSString *)password reply:(void (^)(NSError *))reply +{ + [self generateGitHubAPIToken:username password:password twoFactorCode:nil reply:reply]; +} + ++ (void)generateGitHubAPIToken:(NSString *)username password:(NSString *)password twoFactorCode:(NSString *)twoFactorCode reply:(void (^)(NSError *))reply { NSString *tokenFile = nil; if (![self apiTokenFileExists:&tokenFile]) { - - // Headers & Parameters - NSDictionary *headers = @{ - @"Accept" : @"application/vnd.github.v3+json", - @"User-Agent" : @"AutoPkg", - }; + AFHTTPRequestOperationManager *manager = [self tokenRequestManager:username password:password twoFactorCode:twoFactorCode]; NSDictionary *parameters = @{ @"note" : @"AutoPkg CLI" }; - // Networking - AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://api.github.com"]]; + [manager POST:@"/authorizations" + parameters:parameters + success:^(AFHTTPRequestOperation *operation, NSDictionary *responseObject) { + NSError *error = nil; + NSString *tokenString = responseObject[@"token"]; - // Request Serializer - manager.requestSerializer = [AFJSONRequestSerializer serializer]; - [manager.requestSerializer setAuthorizationHeaderFieldWithUsername:username password:password]; - [headers enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { - [manager.requestSerializer setValue:obj forHTTPHeaderField:key]; - }]; + if (tokenString.length) { + [tokenString writeToFile:tokenFile atomically:YES encoding:NSUTF8StringEncoding error:&error]; + } + reply(error); - // Response serializer - manager.responseSerializer = [AFJSONResponseSerializer serializer]; - - [manager POST:@"/authorizations" parameters:parameters success:^(AFHTTPRequestOperation *operation, NSDictionary *responseObject) { - NSError *error = nil; - NSString *tokenString = responseObject[@"token"]; - - if (tokenString.length){ - [tokenString writeToFile:tokenFile atomically:YES encoding:NSUTF8StringEncoding error:&error]; - reply(error); - } else { - reply(error); } - } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - reply(operation.response ? [LGError errorWithResponse:operation.response] : error); - }]; + failure:^(AFHTTPRequestOperation *operation, NSError *error) { + NSDictionary *headers = operation.response.allHeaderFields; + if (operation.response.statusCode == 401 && [headers[@"X-GitHub-OTP"] hasPrefix:@"required"]) { + NSString *tfa = [self promptForTwoFactorAuthCode]; + if (tfa.length) { + [self generateGitHubAPIToken:username password:password twoFactorCode:tfa reply:reply]; + } else { + reply([LGAutoPkgErrorHandler errorWithGitHubAPIErrorCode:kLGAutoPkgErrorGHApi2FAAuthRequired]); + } + } else { + reply(operation.response ? [LGError errorWithResponse:operation.response] : error); + } + }]; } else { reply(nil); } @@ -1064,55 +1057,114 @@ + (void)generateGitHubAPIToken:(NSString *)username password:(NSString *)passwor + (void)deleteGitHubAPIToken:(NSString *)username password:(NSString *)password reply:(void (^)(NSError *))reply { - AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://api.github.com"]]; + [self deleteGitHubAPIToken:username password:password twoFactorCode:nil reply:reply]; +} - // Request Serializer - manager.requestSerializer = [AFJSONRequestSerializer serializer]; - [manager.requestSerializer setAuthorizationHeaderFieldWithUsername:username - password:password]; ++ (void)deleteGitHubAPIToken:(NSString *)username password:(NSString *)password twoFactorCode:(NSString *)twoFactorCode reply:(void (^)(NSError *))reply +{ + AFHTTPRequestOperationManager *manager = [self tokenRequestManager:username password:password twoFactorCode:twoFactorCode]; - [manager GET:@"/authorizations" parameters:nil success:^(AFHTTPRequestOperation *operation, NSArray *responseObject) { - // Get Auth ID - __block NSDictionary *autoPkgTokenDict = nil; - NSString *token = [self apiToken]; - NSString *tokenLastEight = [token substringFromIndex:token.length - 8]; + [manager GET:@"/authorizations" + parameters:nil + success:^(AFHTTPRequestOperation *operation, NSArray *responseObject) { + // Get Auth ID + __block NSDictionary *autoPkgTokenDict = nil; + NSString *token = [self apiToken]; + NSString *tokenLastEight = [token substringFromIndex:token.length - 8]; - [responseObject enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop) { + [responseObject enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop) { if ([@"AutoPkg CLI" isEqualToString:obj[@"note"]] && - [tokenLastEight isEqualToString:obj[@"token_last_eight"]]) { + [tokenLastEight isEqualToString:obj[@"token_last_eight"]]) { autoPkgTokenDict = obj; *stop = YES; } + }]; + + if (autoPkgTokenDict) { + NSString *delete = [NSString stringWithFormat:@"/authorizations/%@", autoPkgTokenDict[@"id"]]; + + /* Delete the token from the remote */ + [manager DELETE:delete + parameters:@{} + success:^(AFHTTPRequestOperation *operation, id responseObject) { + // remove the file + NSString *tokenFile = nil; + NSError *error = nil; + if ([self apiTokenFileExists:&tokenFile]) { + [[NSFileManager defaultManager] removeItemAtPath:tokenFile error:&error]; + } + reply(error); + } + failure:^(AFHTTPRequestOperation *operation, NSError *error) { + reply(operation.response ? [LGError errorWithResponse:operation.response] : error); + }]; + } else { + reply([LGAutoPkgErrorHandler errorWithGitHubAPIErrorCode:kLGAutoPkgErrorAPITokenNotOnRemote]); + } + } + failure:^(AFHTTPRequestOperation *operation, NSError *error) { + + NSDictionary *headers = operation.response.allHeaderFields; + if (operation.response.statusCode == 401 && [headers[@"X-GitHub-OTP"] hasPrefix:@"required"]) { + // GitHub only seems to send 2FA with POST request, not GET (as of7/25/15 ) + // Do the request again this time specifying POST. + [manager POST:@"/authorizations" + parameters:nil + success:^(AFHTTPRequestOperation *operation, id responseObject) { + // We should never get here, but in case we do send a reply so the UI doesn't hang. + reply(nil); + } + failure:^(AFHTTPRequestOperation *operation, NSError *error) { + NSString *newTwoFactorCode = [self promptForTwoFactorAuthCode]; + if (newTwoFactorCode.length) { + [self deleteGitHubAPIToken:username password:password twoFactorCode:newTwoFactorCode reply:reply]; + } else { + reply([LGAutoPkgErrorHandler errorWithGitHubAPIErrorCode:kLGAutoPkgErrorGHApi2FAAuthRequired]); + } + }]; + } else { + reply(operation.response ? [LGError errorWithResponse:operation.response] : error); + } }]; +} - if (autoPkgTokenDict) { - NSString *delete = [NSString stringWithFormat:@"/authorizations/%@", autoPkgTokenDict[@"id"]]; ++ (AFHTTPRequestOperationManager *)tokenRequestManager:(NSString *)username password:(NSString *)password twoFactorCode:(NSString *)twoFactorCode +{ + AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://api.github.com"]]; - /* Delete the token from the remote */ - [manager DELETE:delete parameters:@{} success:^(AFHTTPRequestOperation *operation, id responseObject) { - // remove the file - NSString *tokenFile = nil; - NSError *error = nil; - if ([self apiTokenFileExists:&tokenFile]) { - [[NSFileManager defaultManager] removeItemAtPath:tokenFile error:&error]; - } - reply(error); - } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - reply(operation.response ? [LGError errorWithResponse:operation.response] : error); - }]; - } else { - NSString *message = LGAutoPkgLocalizedString(@"API Token not found.", nil); - NSString *suggestion = LGAutoPkgLocalizedString(@"A GitHub API token matching the local one was not found on the remote. You may need to remove it manually. If you've just added the token, you may need to wait a minute to delete it.", nil); - - NSError *error = [NSError errorWithDomain:[[NSProcessInfo processInfo] processName] - code:-2 - userInfo:@{NSLocalizedDescriptionKey : message, - NSLocalizedRecoverySuggestionErrorKey : suggestion}]; - reply(error); - } - } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - reply(operation.response ? [LGError errorWithResponse:operation.response] : error); + manager.responseSerializer = [AFJSONResponseSerializer serializer]; + manager.requestSerializer = [AFJSONRequestSerializer serializer]; + + [manager.requestSerializer setAuthorizationHeaderFieldWithUsername:username + password:password]; + + NSDictionary *defaultHeaders = @{ + @"Accept" : @"application/vnd.github.v3+json", + @"User-Agent" : @"AutoPkg", + }; + + [defaultHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + [manager.requestSerializer setValue:obj forHTTPHeaderField:key]; }]; + + if (twoFactorCode) { + [manager.requestSerializer setValue:twoFactorCode forHTTPHeaderField:@"X-GitHub-OTP"]; + } + + return manager; +} + ++ (NSString *)promptForTwoFactorAuthCode +{ + NSString *code; + LGTwoFactorAuthAlert *alert = [[LGTwoFactorAuthAlert alloc] init]; + + NSModalResponse button = [alert runModal]; + if (button == NSAlertFirstButtonReturn) { + code = [alert authorizatoinCode]; + } + + return code; } #pragma mark-- Convenience Initializers -- @@ -1171,11 +1223,37 @@ + (LGAutoPkgTask *)repoDeleteTask:(NSString *)repo; } #pragma mark-- Other Methods -- ++ (NSString *)version +{ + NSString *version; + + NSTask *task = [[NSTask alloc] init]; + + task = task; + task.launchPath = @"/usr/bin/python"; + task.arguments = @[ autopkg(), @"version" ]; + task.standardOutput = [NSPipe pipe]; + [task launch]; + [task waitUntilExit]; + + NSData *data = [[task.standardOutput fileHandleForReading] availableData]; + if (data.length) { + version = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + } + + return [version stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] ?: @"0.0.0"; +} + + (BOOL)instanceIsRunning { NSArray *runningArgs = @[ autopkg(), @"run", @"--recipe-list" ]; - return ([BSDProcessInfo processWithName:@"Python" - matchingArguments:runningArgs] != nil); + NSArray *matchedProcs = [BSDProcessInfo allProcessesWithName:@"Python" + matchingArguments:runningArgs]; + + if (matchedProcs.count) { + DLog(@"Another autopkg run recipe process detected: %@", matchedProcs); + } + return (matchedProcs.count != 0); } #pragma mark - Task Status Update Delegate diff --git a/AutoPkgr/Models/Email & Notifications/LGAutoPkgReport.h b/AutoPkgr/Models/Email & Notifications/LGAutoPkgReport.h index d25cd3f8..42e384ac 100644 --- a/AutoPkgr/Models/Email & Notifications/LGAutoPkgReport.h +++ b/AutoPkgr/Models/Email & Notifications/LGAutoPkgReport.h @@ -79,6 +79,11 @@ typedef NS_ENUM(NSInteger, LGReportIntegrationFrequency) { */ @property (copy, nonatomic, readonly) NSString *emailSubjectString; +/** + * Formatted message suitable for Slack / HipChat (formatted as text) + */ +@property (copy, nonatomic, readonly) NSString *webChannelMessageString; + /** * Check to determine if there is anything to report */ diff --git a/AutoPkgr/Models/Email & Notifications/LGAutoPkgReport.m b/AutoPkgr/Models/Email & Notifications/LGAutoPkgReport.m index ca25579e..fe165487 100644 --- a/AutoPkgr/Models/Email & Notifications/LGAutoPkgReport.m +++ b/AutoPkgr/Models/Email & Notifications/LGAutoPkgReport.m @@ -118,16 +118,16 @@ - (BOOL)updatesToReport - (NSString *)emailSubjectString { - if ([_reportDictionary[kReportKeySummaryResults] count] > 0) { - return quick_formatString(NSLocalizedString(@"New software available for testing", nil)); + if ([_reportDictionary[kReportKeySummaryResults][kReportProcessorURLDownloader] count] > 0) { + return NSLocalizedString(@"New software available for testing", nil); } else if ([_reportDictionary[kReportKeyFailures] count] > 0) { - return quick_formatString(NSLocalizedString(@"Failures occurred while running AutoPkg", nil)); + return NSLocalizedString(@"Failures occurred while running AutoPkg", nil); } else if (self.error) { - return quick_formatString(NSLocalizedString(@"An error occurred while running AutoPkg", nil)); + return NSLocalizedString(@"An error occurred while running AutoPkg", nil); } else if (_integrations) { for (LGIntegration *integration in _integrations) { if (integration.info.status == kLGIntegrationUpdateAvailable) { - return quick_formatString(NSLocalizedString(@"Update to helper components available", nil)); + return NSLocalizedString(@"Update to helper components available", nil); } } } @@ -172,6 +172,21 @@ - (NSString *)emailMessageString return [message copy]; } +- (NSString *)webChannelMessageString { + NSMutableString *message = self.emailSubjectString.mutableCopy; + [message appendString:@":\n"]; + + [self.updatedApplications enumerateObjectsUsingBlock:^(LGUpdatedApplication *app, NSUInteger idx, BOOL *stop) { + [message appendFormat:@" * %@ [%@]\n", app.name, app.version]; // Format howerver + }]; + + NSError *error = self.failureError; + if (error) { + [message appendFormat:@"\n%@\n%@", error.localizedDescription, error.localizedRecoverySuggestion]; + } + + return message.copy ?: @""; +} #pragma mark - Private - (NSString *)overviewString { diff --git a/AutoPkgr/Models/Email & Notifications/LGEmailNotification.m b/AutoPkgr/Models/Email & Notifications/LGEmailNotification.m index 23e2f1d8..9cc4ef3b 100644 --- a/AutoPkgr/Models/Email & Notifications/LGEmailNotification.m +++ b/AutoPkgr/Models/Email & Notifications/LGEmailNotification.m @@ -149,6 +149,9 @@ - (void)sendEmailNotification:(NSString *)subject message:(NSString *)message te NSString *fullSubject = quick_formatString(@"%@ on %@", subject, [NSHost currentHost].localizedName); void (^didCompleteSendOperation)(NSError *) = ^(NSError *error) { + if (error) { + NSLog(@"Error sending email: %@", error); + } if (self.notificatonComplete) { self.notificatonComplete(error); } @@ -172,6 +175,7 @@ - (void)sendEmailNotification:(NSString *)subject message:(NSString *)message te MCOSMTPSession *session = [[MCOSMTPSession alloc] init]; session.hostname = credential.server; session.port = (int)credential.port; + session.timeout = 15; if (credential.user && credential.password) { session.username = credential.user; diff --git a/AutoPkgr/Models/Email & Notifications/LGHipChatNotification.m b/AutoPkgr/Models/Email & Notifications/LGHipChatNotification.m index 3b6c25b1..f0001cf2 100644 --- a/AutoPkgr/Models/Email & Notifications/LGHipChatNotification.m +++ b/AutoPkgr/Models/Email & Notifications/LGHipChatNotification.m @@ -24,7 +24,7 @@ static NSString *const HipChatLink = @"https://hipchat.com/rooms"; -static NSString *const HipChatNotificationEnabledKey = @"HipChatNotificationEnabled"; +static NSString *const HipChatNotificationEnabledKey = @"HipChatNotificationsEnabled"; static NSString *const HipChatNotificationRoomKey = @"HipChatNotificationRoom"; static NSString *const HipChatNotificationNotifyKey = @"HipChatNotificationNotify"; @@ -67,7 +67,6 @@ - (void)send:(void (^)(NSError *))complete { self.notificatonComplete = complete; - NSString *message = self.report.emailSubjectString; NSString *color; if (self.report.error) { color = @"red"; @@ -75,9 +74,11 @@ - (void)send:(void (^)(NSError *))complete color = @"green"; } - NSDictionary *parameters = @{ @"message" : message, - @"color" : color - }; + NSDictionary *parameters = @{ @"message" : self.report.webChannelMessageString, + @"color" : color, + @"message_format" : @"text" + }; + [self sendMessageWithParameters:[self baseRoomPostParameters:parameters]]; } @@ -145,7 +146,8 @@ - (void)sendMessageWithParameters:(NSDictionary *)parameters // Error here means there was a problem getting the password. self.notificatonComplete(error); } else { - NSString *urlString = [self sendNotificationPath]; + NSString *urlString = [[self sendNotificationPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + [manager POST:urlString parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { // self.notificatonComplete(nil); diff --git a/AutoPkgr/Models/Email & Notifications/LGSlackNotification.m b/AutoPkgr/Models/Email & Notifications/LGSlackNotification.m index c2c667b4..213e9e44 100644 --- a/AutoPkgr/Models/Email & Notifications/LGSlackNotification.m +++ b/AutoPkgr/Models/Email & Notifications/LGSlackNotification.m @@ -63,15 +63,7 @@ - (void)send:(void (^)(NSError *))complete self.notificatonComplete = complete; } - NSMutableString *str = self.report.emailSubjectString.mutableCopy; - [str appendString:@":\n"]; - - [self.report.updatedApplications enumerateObjectsUsingBlock:^(LGUpdatedApplication *app, NSUInteger idx, BOOL *stop) { - [str appendFormat:@" * %@ [%@]\n", app.name, app.version]; // Format howerver - }]; - - NSDictionary *slackParameters = @{ @"text" : str }; - + NSDictionary *slackParameters = @{ @"text" : self.report.webChannelMessageString }; [self sendMessageWithParameters:slackParameters]; } @@ -126,7 +118,7 @@ - (void)sendMessageWithParameters:(NSDictionary *)parameters [manager POST:webHookURL parameters:[self baseParameters:parameters] success:^(AFHTTPRequestOperation *operation, id responseObject) { self.notificatonComplete(nil); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - NSLog(@"%@", operation.responseString); + NSLog(@"Error sending Slack notification: %@", operation.responseString); self.notificatonComplete([LGError errorWithResponse:operation.response]); }]; } diff --git a/AutoPkgr/Models/Email & Notifications/Notification Manager/LGNotificationManager.m b/AutoPkgr/Models/Email & Notifications/Notification Manager/LGNotificationManager.m index daeb435b..2601e6b8 100644 --- a/AutoPkgr/Models/Email & Notifications/Notification Manager/LGNotificationManager.m +++ b/AutoPkgr/Models/Email & Notifications/Notification Manager/LGNotificationManager.m @@ -60,10 +60,9 @@ - (instancetype)initWithReportDictionary:(NSDictionary *)dictionary errors:(NSEr - (void)sendEnabledNotifications:(void (^)(NSError *))complete; { - dispatch_queue_t autopkgr_notification_manager_queue; - autopkgr_notification_manager_queue = dispatch_queue_create("com.lindegroup.autopkgr.notification.manager.queue", DISPATCH_QUEUE_CONCURRENT); - dispatch_async(autopkgr_notification_manager_queue, ^{ + NSOperationQueue *queue = [[NSOperationQueue alloc] init]; + [queue addOperationWithBlock:^{ NSMutableArray *enabledServices = [NSMutableArray arrayWithCapacity:serviceClasses().count]; @@ -135,7 +134,7 @@ - (void)sendEnabledNotifications:(void (^)(NSError *))complete; completedService(); }]; } - }); + }]; } - (NSError *)processedError diff --git a/AutoPkgr/Models/Integrations/LGAbsoluteManageIntegration.m b/AutoPkgr/Models/Integrations/LGAbsoluteManageIntegration.m index 13ab31e7..7c7851c3 100644 --- a/AutoPkgr/Models/Integrations/LGAbsoluteManageIntegration.m +++ b/AutoPkgr/Models/Integrations/LGAbsoluteManageIntegration.m @@ -39,7 +39,7 @@ + (NSString *)name + (NSString *)shortName { - return @"AMExporter"; + return @"AMExport"; } + (NSString *)credits { return @"Copyright 2014 Thomas Burgin\nhttp://www.apache.org/licenses/LICENSE-2.0"; diff --git a/AutoPkgr/Models/Integrations/LGGitIntegration.m b/AutoPkgr/Models/Integrations/LGGitIntegration.m index f38f90dc..622ad571 100644 --- a/AutoPkgr/Models/Integrations/LGGitIntegration.m +++ b/AutoPkgr/Models/Integrations/LGGitIntegration.m @@ -22,7 +22,7 @@ #import "LGDefaults.h" #import "NSData+taskData.h" - +#import "LGLogger.h" #import static NSString *const kLGOfficialGit = @"/usr/local/git/bin/git"; @@ -179,7 +179,7 @@ + (void)gitTaskWithArguments:(NSArray *)args repoPath:(NSString *)repoPath reply NSString *binary = [self binary]; if (access(binary.UTF8String, X_OK) != 0) { - reply(nil, [self gitErrorWithMessage:@"Could not locate the git binary, or it was not executable" code:kLGGitErrorNotInstalled]); + reply(nil, [self gitErrorWithMessage:@"Could not locate the git binary, or it was not executable" repo:repoPath code:kLGGitErrorNotInstalled]); } __block NSMutableData *outData = [[NSMutableData alloc] init]; @@ -195,7 +195,29 @@ + (void)gitTaskWithArguments:(NSArray *)args repoPath:(NSString *)repoPath reply task.standardOutput = [NSPipe pipe]; task.standardError = [NSPipe pipe]; - [task useSystemProxiesForDestination:@"github.com"]; + + LGDefaults *defaults = [LGDefaults standardUserDefaults]; + if ([defaults boolForKey:@"useSystemProxies"]) { + [task useSystemProxiesForDestination:@"https://github.com"]; + } else { + NSString *httpProxy = [defaults objectForKey:@"HTTP_PROXY"]; + NSString *httpsProxy = [defaults objectForKey:@"HTTPS_PROXY"]; + NSString *noProxy = [defaults objectForKey:@"NO_PROXY"]; + + if (httpProxy || httpsProxy) { + NSMutableDictionary *env = [[[NSProcessInfo processInfo] environment] mutableCopy]; + if (httpProxy) { + env[@"HTTP_PROXY"] = httpProxy; + } + if (httpsProxy) { + env[@"HTTPS_PROXY"] = httpsProxy; + } + if (noProxy) { + env[@"NO_PROXY"] = noProxy; + } + task.environment = env.copy; + } + } NSFileHandle *outHandle = [task.standardOutput fileHandleForReading]; [outHandle setReadabilityHandler:^(NSFileHandle *fh) { @@ -226,6 +248,7 @@ + (void)gitTaskWithArguments:(NSArray *)args repoPath:(NSString *)repoPath reply } NSError *error = [self gitErrorWithMessage:errData.taskData_string + repo:repoPath code:aTask.terminationStatus]; dispatch_async(git_callback_queue, ^{ @@ -245,12 +268,13 @@ + (void)gitTaskWithArguments:(NSArray *)args repoPath:(NSString *)repoPath reply [task launch]; } -+ (NSError *)gitErrorWithMessage:(NSString *)message code:(NSInteger)code { ++ (NSError *)gitErrorWithMessage:(NSString *)message repo:(NSString *)repo code:(NSInteger)code { NSError *error = nil; if (code != 0) { + error = [NSError errorWithDomain:@"AutoPkgr Git" code:code - userInfo:@{NSLocalizedDescriptionKey:@"There was a problem executing git.", + userInfo:@{NSLocalizedDescriptionKey: quick_formatString( @"There was a problem executing git on %@", repo), NSLocalizedRecoverySuggestionErrorKey:message ?: @""}]; } return error; diff --git a/AutoPkgr/Models/Integrations/LGIntegration Base/LGIntegration.m b/AutoPkgr/Models/Integrations/LGIntegration Base/LGIntegration.m index ccb5795c..c7ff8bd8 100644 --- a/AutoPkgr/Models/Integrations/LGIntegration Base/LGIntegration.m +++ b/AutoPkgr/Models/Integrations/LGIntegration Base/LGIntegration.m @@ -132,14 +132,13 @@ - (void)dealloc _progressUpdateBlock = nil; _replyErrorBlock = nil; - /* Repoint so we don't loose reference to the _infoUpdateBlockDict after dealloc */ + DevLog(@"Starting Dealloc %@", self); NSMutableDictionary *releaseDict = _infoUpdateBlocksDict; - dispatch_async(autopkgr_integration_synchronizer_queue(), ^{ - [releaseDict enumerateKeysAndObjectsUsingBlock:^(void (^infoUpdate)(LGIntegrationInfo *), id obj, BOOL * stop) { + [releaseDict enumerateKeysAndObjectsUsingBlock:^(void (^infoUpdate)(LGIntegrationInfo *), id obj, BOOL * stop) { infoUpdate = nil; - }]; - [releaseDict removeAllObjects]; - }); + }]; + [releaseDict removeAllObjects]; + DevLog(@"Done Dealloc %@", self); } - (instancetype)init @@ -261,8 +260,8 @@ - (void)getInfo:(void (^)(LGIntegrationInfo *))reply _isRefreshing = YES; void (^updateInfoHandlers)() = ^() { dispatch_async(autopkgr_integration_synchronizer_queue(), ^{ + self.info = [[LGIntegrationInfo alloc] initWithIntegration:self]; if (reply || _infoUpdateHandler) { - self.info = [[LGIntegrationInfo alloc] initWithIntegration:self]; dispatch_async(dispatch_get_main_queue(), ^{ if (_infoUpdateHandler) { _infoUpdateHandler(_info); @@ -271,8 +270,6 @@ - (void)getInfo:(void (^)(LGIntegrationInfo *))reply reply(_info); } }); - } else { - self.info = [[LGIntegrationInfo alloc] initWithIntegration:self]; } _isRefreshing = NO; }); diff --git a/AutoPkgr/Models/Integrations/LGJSSImporterIntegration.h b/AutoPkgr/Models/Integrations/LGJSSImporterIntegration.h index 2236af96..8d1a22ef 100644 --- a/AutoPkgr/Models/Integrations/LGJSSImporterIntegration.h +++ b/AutoPkgr/Models/Integrations/LGJSSImporterIntegration.h @@ -41,8 +41,6 @@ extern NSString* const kLGJSSDistPointTypeKey; + (instancetype)standardUserDefaults __attribute__((unavailable("Cannot use the shared object in this subclass."))); -@property (copy, nonatomic) LGHTTPCredential *jssCredentials; - @property (copy, nonatomic) NSString *JSSURL; @property (copy, nonatomic) NSString *JSSAPIUsername; @property (copy, nonatomic) NSString *JSSAPIPassword; diff --git a/AutoPkgr/Models/Integrations/LGJSSImporterIntegration.m b/AutoPkgr/Models/Integrations/LGJSSImporterIntegration.m index d3fc51f5..91f70be4 100644 --- a/AutoPkgr/Models/Integrations/LGJSSImporterIntegration.m +++ b/AutoPkgr/Models/Integrations/LGJSSImporterIntegration.m @@ -104,6 +104,14 @@ - (NSString *)installedVersion return _installedVersion; } +- (void)customInstallActions:(void (^)(NSError *))reply { + LGJSSImporterDefaults *defaults = [[LGJSSImporterDefaults alloc] init]; + NSNumber *verifySSL = [defaults autoPkgDomainObject:@"JSS_VERIFY_SSL"]; + if (!verifySSL) { + defaults.JSSVerifySSL = YES; + } +} + - (void)customUninstallActions:(void (^)(NSError *))reply { // Clear out the defaults... LGJSSImporterDefaults *defaults = [[LGJSSImporterDefaults alloc] init]; @@ -121,41 +129,6 @@ - (void)customUninstallActions:(void (^)(NSError *))reply { #pragma mark - LGDefaults category implementation for JSSImporter Interface @implementation LGJSSImporterDefaults -@synthesize jssCredentials = _jssCredentials; - - --(void)setJssCredentials:(LGHTTPCredential *)jssCredentials { - _jssCredentials = jssCredentials; - [self configureCredentialSaveBlock:jssCredentials]; - [jssCredentials save]; -} - -- (LGHTTPCredential *)jssCredentials { - if (!_jssCredentials) { - _jssCredentials = [[LGHTTPCredential alloc] init]; - - _jssCredentials.server = self.JSSURL; - _jssCredentials.user = self.JSSAPIUsername; - _jssCredentials.password = self.JSSAPIPassword; - - /* If the BOOL verifySSL is set to true set this to OS implicit trust. - * Otherwise treat the trust setting as if it was confirmed by the user */ - _jssCredentials.sslTrustSetting = self.JSSVerifySSL ? kLGSSLTrustOSImplicitTrust : kLGSSLTrustUserConfirmedTrust; - - [self configureCredentialSaveBlock:_jssCredentials]; - } - return _jssCredentials; -} - -- (void)configureCredentialSaveBlock:(LGHTTPCredential *)credential { - if (!credential.saveBlock) { - credential.saveBlock = ^(LGHTTPCredential *cred) { - self.JSSAPIUsername = cred.user; - self.JSSAPIPassword = cred.password; - self.JSSURL = cred.server; - }; - } -} - (NSString *)JSSURL { diff --git a/AutoPkgr/Models/Passwords-Credentials/LGServerCredentials.m b/AutoPkgr/Models/Passwords-Credentials/LGServerCredentials.m index 3bf11f76..087106b9 100644 --- a/AutoPkgr/Models/Passwords-Credentials/LGServerCredentials.m +++ b/AutoPkgr/Models/Passwords-Credentials/LGServerCredentials.m @@ -130,6 +130,14 @@ - (NSURL *)serverURL return _serverURL; } +- (NSString *)host { + return self.serverURL.host; +} + +- (NSString *)protocol { + return self.serverURL.scheme; +} + - (NSInteger)port { return (_port != 0) ? _port : self.serverURL.port.integerValue; @@ -202,12 +210,13 @@ - (void)handleCertificateTrustChallenge:(NSURLAuthenticationChallenge *)challeng } } + [[LGCertificateStore sharedStorage] setTrust:_sslTrustSetting forHost:self.host]; + if (proceed) { NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; [challenge.sender useCredential:credential forAuthenticationChallenge:challenge]; - [[LGCertificateStore sharedStorage] setTrust:_sslTrustSetting forHost:self.host]; [[LGCertificateStore sharedStorage] setChallenge:challenge forHost:self.host]; } else { @@ -229,8 +238,9 @@ - (void)checkCredentialsForPath:(NSString *)path reply:(void (^)(LGHTTPCredentia } // String encode the path. - path = [path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - NSURL *url = [NSURL URLWithString:path relativeToURL:self.serverURL]; + NSString *fullURL = [[self.server stringByAppendingPathComponent:path] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + + NSURL *url = [NSURL URLWithString:fullURL]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.timeoutInterval = 10.0; @@ -269,10 +279,12 @@ - (void)checkCredentialsForPath:(NSString *)path reply:(void (^)(LGHTTPCredentia }]; [op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { + DLog(@"Successfully checked credentials for %@ at %@", self.user, self.server); purgeProtectedSpace(); reply(self, authWasChallenged ? kLGCredentialChallengeSuccess : kLGCredentialsNotChallenged, nil); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { purgeProtectedSpace(); + DLog(@"Credential verification failed for %@ at %@. Error: %@", self.user, self.server, error.localizedDescription); reply(self, kLGCredentialChallengeFailed, error); }]; diff --git a/AutoPkgr/Supporting Files/AutoPkgr-Info.plist b/AutoPkgr/Supporting Files/AutoPkgr-Info.plist index b7094578..0a89e9ab 100755 --- a/AutoPkgr/Supporting Files/AutoPkgr-Info.plist +++ b/AutoPkgr/Supporting Files/AutoPkgr-Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3 + 1.3.1 CFBundleSignature ???? CFBundleVersion - 1002 + 1075 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/AutoPkgr/Supporting Files/en.lproj/Credits.rtf b/AutoPkgr/Supporting Files/en.lproj/Credits.rtf index c40f1381..0fe164d1 100644 --- a/AutoPkgr/Supporting Files/en.lproj/Credits.rtf +++ b/AutoPkgr/Supporting Files/en.lproj/Credits.rtf @@ -1,93 +1,85 @@ {\rtf1\ansi\ansicpg1252\cocoartf1348\cocoasubrtf170 -{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Light;\f1\fnil\fcharset0 HelveticaNeue;\f2\fnil\fcharset0 HelveticaNeue-Medium; -} +{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Light;\f1\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \vieww9600\viewh8400\viewkind0 -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc +\deftab720 +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc -\f0\fs24 \cf0 \ -AutoPkgr was created by -\f1\b \ +\f0\fs22 \cf0 \expnd0\expndtw0\kerning0 \ -James Barclay -\f0\b0 \ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc -\cf0 Engineering, interface design, testing, documentation.\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc -\cf0 \ +AutoPkgr was created at the\ +Linde Group in Emeryville, California.\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc -\f1\b Elliot Jordan\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc +\fs20 \cf0 \expnd0\expndtw0\kerning0 +({\field{\*\fldinst{HYPERLINK "https://sfbay.craigslist.org/search/jjj?sort=rel&query=linde"}}{\fldrslt \expnd0\expndtw0\kerning0 +\ul \ulc0 We\'92re hiring!}})\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc -\f0\b0 \cf0 Interface design, testing, documentation, graphics.\ +\fs22 \cf0 \expnd0\expndtw0\kerning0 \ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc -\f1\b \cf0 Josh Senick\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc +\f1 \cf0 \expnd0\expndtw0\kerning0 +Original AutoPkgr team:\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc -\f0\b0 \cf0 Engineering, testing, logical thinking.\ +\f0 \cf0 \expnd0\expndtw0\kerning0 +James Barclay, Elliot Jordan, and Josh Senick\ \ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc -\f1\b \cf0 Eldon Ahrold\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc +\f1 \cf0 \expnd0\expndtw0\kerning0 +Ongoing development:\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc -\f0\b0 \cf0 Engineering, testing.\ -\ -\ -\ -AutoPkgr takes advantage of the\ -following awesome open-source libraries:\ -\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc - -\f1\b \cf0 MailCore2 -\f0\b0 \ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc -{\field{\*\fldinst{HYPERLINK "https://github.com/MailCore/mailcore2"}}{\fldrslt \cf0 https://github.com/MailCore/mailcore2}}\ -\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc +\f0 \cf0 \expnd0\expndtw0\kerning0 +Eldon Ahrold, Elliot Jordan, and James Barclay\ +\ +AutoPkgr takes advantage of the following awesome open-source libraries and projects:\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc +{\field{\*\fldinst{HYPERLINK "https://github.com/AFNetworking/AFNetworking"}}{\fldrslt \cf0 \expnd0\expndtw0\kerning0 +\ul \ulc0 AFNetworking}}\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc +{\field{\*\fldinst{HYPERLINK "https://github.com/eahrold/AHKeychain"}}{\fldrslt \cf0 \expnd0\expndtw0\kerning0 +\ul \ulc0 AHKeychain}}\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc +{\field{\*\fldinst{HYPERLINK "https://github.com/eahrold/AHLaunchCtl"}}{\fldrslt \cf0 \expnd0\expndtw0\kerning0 +\ul \ulc0 AHLaunchCtl}}\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc +{\field{\*\fldinst{HYPERLINK "https://github.com/eahrold/AHProxySettings"}}{\fldrslt \cf0 \expnd0\expndtw0\kerning0 +\ul \ulc0 AHProxySettings}}\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc +{\field{\*\fldinst{HYPERLINK "http://fontawesome.io/"}}{\fldrslt \cf0 \expnd0\expndtw0\kerning0 +\ul \ulc0 FontAwesome}}\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc +{\field{\*\fldinst{HYPERLINK "https://github.com/MailCore/mailcore2"}}{\fldrslt \cf0 \expnd0\expndtw0\kerning0 +\ul \ulc0 MailCore2}}\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc +{\field{\*\fldinst{HYPERLINK "https://github.com/RNCryptor/RNCryptor"}}{\fldrslt \cf0 \expnd0\expndtw0\kerning0 +\ul \ulc0 RNCryptor}}\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc +{\field{\*\fldinst{HYPERLINK "https://github.com/google/santa"}}{\fldrslt \cf0 \expnd0\expndtw0\kerning0 +\ul \ulc0 SNTCodesignChecker}}\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc +{\field{\*\fldinst{HYPERLINK "http://sparkle-project.org"}}{\fldrslt \cf0 \expnd0\expndtw0\kerning0 +\ul \ulc0 Sparkle}}\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc +{\field{\*\fldinst{HYPERLINK "https://github.com/nicklockwood/XMLDictionary"}}{\fldrslt \cf0 \expnd0\expndtw0\kerning0 +\ul \ulc0 XMLDictionary}}\ +\ +And of course, we owe a huge thanks to everybody involved with {\field{\*\fldinst{HYPERLINK "https://autopkg.github.io/autopkg/"}}{\fldrslt \expnd0\expndtw0\kerning0 +\ul \ulc0 AutoPkg}}.\ +\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc -\f1\b \cf0 Sparkle -\f0\b0 \ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc -{\field{\*\fldinst{HYPERLINK "https://github.com/soffes/sskeychain"}}{\fldrslt \cf0 http://sparkle-project.org}}\ -\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc +\f1\fs28 \cf0 \expnd0\expndtw0\kerning0 +The Linde Group\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc -\f1\b \cf0 AFNetworking -\f0\b0 \ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc -{\field{\*\fldinst{HYPERLINK "https://github.com/soffes/sskeychain"}}{\fldrslt \cf0 https://github.com/AFNetworking/AFNetworking}}\ -\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc - -\f1\b \cf0 SNTCodesignChecker -\f0\b0 \ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc -{\field{\*\fldinst{HYPERLINK "https://github.com/soffes/sskeychain"}}{\fldrslt \cf0 https://github.com/google/santa}}\ -\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc - -\f1\b \cf0 RNCryptor -\f0\b0 \ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc -{\field{\*\fldinst{HYPERLINK "https://github.com/soffes/sskeychain"}}{\fldrslt \cf0 https://github.com/RNCryptor/RNCryptor}}\ -\ -\ -\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc - -\f2\fs28 \cf0 The Linde Group -\fs24 \ - -\f0\fs20 Technology \'95\'a0Strategy \'95\'a0Integrity\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc -{\field{\*\fldinst{HYPERLINK "http://www.lindegroup.com"}}{\fldrslt \cf0 www.lindegroup.com}}\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc - -\fs24 \cf0 \ -\ -\ +\f0\fs20 \cf0 \expnd0\expndtw0\kerning0 +Technology \'95\'a0Strategy \'95\'a0Integrity\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\qc +{\field{\*\fldinst{HYPERLINK "http://www.lindegroup.com"}}{\fldrslt \cf0 \expnd0\expndtw0\kerning0 +\ul \ulc0 www.lindegroup.com}}\ } \ No newline at end of file diff --git a/AutoPkgr/Supporting Files/en.lproj/LocalizableHelpPopover.strings b/AutoPkgr/Supporting Files/en.lproj/LocalizableHelpPopover.strings index 6710bf7f..69ec4f36 100644 --- a/AutoPkgr/Supporting Files/en.lproj/LocalizableHelpPopover.strings +++ b/AutoPkgr/Supporting Files/en.lproj/LocalizableHelpPopover.strings @@ -13,7 +13,7 @@ "recipeTableInfo" = "Check the box next to the name of a recipe in order to add the recipe to your AutoPkg run schedule.\n\nControl-click on a recipe to bring up a contextual menu with options to get info, run the recipe, or create an override. You can also set the preferred recipe editor.\n\nIf a caution icon is present in the status column, there is an issue with the recipe. (The most likely cause is a missing parent recipe.)"; "slackHelpInfoTitle" = "Slack Notifications"; -"slackHelpInfo" = "Incoming Webhooks are a simple way to post messages from external sources into Slack. Slack admins can generate the Webhook URL that is used to post messages to a specific toom. Visit the Slack webhooks documentation (linked below) for more information."; +"slackHelpInfo" = "Incoming Webhooks are a simple way to post messages from external sources into Slack. Slack admins can generate the Webhook URL that is used to post messages to a specific room. Visit the Slack webhooks documentation (linked below) for more information."; "slackHelpInfoURL" = "https://api.slack.com/incoming-webhooks"; diff --git a/AutoPkgr/Supporting Files/main.m b/AutoPkgr/Supporting Files/main.m index 583515b8..6b9f001a 100755 --- a/AutoPkgr/Supporting Files/main.m +++ b/AutoPkgr/Supporting Files/main.m @@ -45,6 +45,7 @@ int main(int argc, const char *argv[]) error:nil state:kLGAutoPkgProgressStart]; + [manager setProgressUpdateBlock:^(NSString *message, double progress) { [[helper.connection remoteObjectProxy] sendMessageToMainApplication:message progress:progress @@ -61,12 +62,6 @@ int main(int argc, const char *argv[]) state:kLGAutoPkgProgressComplete]; // Wrap up the progress messaging... completionMessageSent = YES; - if (!completionMessageSent) { - [[helper.connection remoteObjectProxy] sendMessageToMainApplication:nil - progress:100 - error:nil - state:kLGAutoPkgProgressComplete]; - } LGNotificationManager *notifier = [[LGNotificationManager alloc] initWithReportDictionary:report errors:error]; @@ -80,7 +75,6 @@ int main(int argc, const char *argv[]) [[NSRunLoop currentRunLoop] run]; return 0; - } else { return NSApplicationMain(argc, argv); } diff --git a/AutoPkgr/Utility/LGAutoPkgSchedule.m b/AutoPkgr/Utility/LGAutoPkgSchedule.m index ece6e4bb..47fcf215 100644 --- a/AutoPkgr/Utility/LGAutoPkgSchedule.m +++ b/AutoPkgr/Utility/LGAutoPkgSchedule.m @@ -75,7 +75,7 @@ + (void)startAutoPkgSchedule:(BOOL)start scheduleOrInterval:(id)scheduleOrInterv NSString *program = [[NSProcessInfo processInfo] arguments].firstObject; [[helper.connection remoteObjectProxyWithErrorHandler:^(NSError *error) { - NSLog(@"%@", error); + NSLog(@"[%@] %@ ",[self class], error); reply(error); }] scheduleRun:scheduleOrInterval user:NSUserName() diff --git a/AutoPkgr/Utility/LGError.h b/AutoPkgr/Utility/LGError.h index 069a7bb2..136d6be0 100644 --- a/AutoPkgr/Utility/LGError.h +++ b/AutoPkgr/Utility/LGError.h @@ -58,6 +58,8 @@ typedef NS_ENUM(NSInteger, LGErrorAutoPkgCodes) { /** AutoPkg returns 255 if no recipe is specified */ kLGErrorAutoPkgNoRecipes = 255, + + }; @interface LGError : NSObject diff --git a/AutoPkgr/Utility/LGGitHubJSONLoader.m b/AutoPkgr/Utility/LGGitHubJSONLoader.m index 4bb19ae8..cf85f715 100644 --- a/AutoPkgr/Utility/LGGitHubJSONLoader.m +++ b/AutoPkgr/Utility/LGGitHubJSONLoader.m @@ -88,7 +88,15 @@ - (NSArray *)jsonObject - (NSDictionary *)latestReleaseDictionary { if (!_latestReleaseDictionary) { - _latestReleaseDictionary = [self.jsonObject firstObject]; + [self.jsonObject enumerateObjectsUsingBlock:^(NSDictionary *releaseDict, NSUInteger idx, BOOL *stop) { + NSNumber *prerelease = releaseDict[@"prerelease"]; + if (prerelease.boolValue == NO) { + _latestReleaseDictionary = releaseDict; + *stop = YES; + } else { + NSLog(@"Skipping prerelease version of %@", self.repoURL); + } + }]; } return _latestReleaseDictionary; } @@ -185,10 +193,10 @@ + (NSArray *)getJSONFromURL:(NSString *)aUrl // Create the NSURLRequest object with the given URL NSURLRequest *req = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:15.0]; + timeoutInterval:5.0]; // Initialize our response and error objects - NSURLResponse *resp; + NSHTTPURLResponse *resp; NSError *error = nil; // Get the JSON data @@ -196,7 +204,7 @@ + (NSArray *)getJSONFromURL:(NSString *)aUrl returningResponse:&resp error:&error]; - if (error) { + if (error || resp.statusCode != 200) { NSLog(@"NSURLConnection error when attempting to get JSON data from the GitHub API. Error: %@.", error); return nil; } diff --git a/AutoPkgr/Utility/LGHTTPRequest.h b/AutoPkgr/Utility/LGHTTPRequest.h index 4bfc14b9..68f02832 100644 --- a/AutoPkgr/Utility/LGHTTPRequest.h +++ b/AutoPkgr/Utility/LGHTTPRequest.h @@ -25,6 +25,10 @@ @interface LGHTTPRequest : NSObject - (void)retrieveDistributionPoints:(LGHTTPCredential *)credential - reply:(void (^)(NSDictionary *distributionPoints, NSError *error))reply; + reply:(void (^)(NSDictionary *distributionPoints, NSError *error))reply; + + +- (void)retrieveDistributionPoints2:(LGHTTPCredential *)credential + reply:(void (^)(NSDictionary *distributionPoints, NSError *error))reply; @end diff --git a/AutoPkgr/Utility/LGHTTPRequest.m b/AutoPkgr/Utility/LGHTTPRequest.m index 10015890..8d1b305b 100644 --- a/AutoPkgr/Utility/LGHTTPRequest.m +++ b/AutoPkgr/Utility/LGHTTPRequest.m @@ -36,14 +36,54 @@ - (void)dealloc [self resetCredentials]; } +- (void)retrieveDistributionPoints2:(LGHTTPCredential *)credential + reply:(void (^)(NSDictionary *distributionPoints, NSError *error))reply; +{ + NSLog(@"server = %@", credential.serverURL); + + AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:credential.serverURL]; + + manager.requestSerializer = [AFJSONRequestSerializer serializer]; + [manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"]; + + manager.requestSerializer.timeoutInterval = 5.0; + manager.securityPolicy = [AFSecurityPolicy defaultPolicy]; + if (credential.sslTrustSetting & (kLGSSLTrustUserExplicitTrust | kLGSSLTrustUserConfirmedTrust)) { + // Even in the event the user has the certificate set to trust in their keychain + // that setting doesn't seem to get picked up by python-jss' request module so set verify to NO + manager.securityPolicy.allowInvalidCertificates = YES; + manager.securityPolicy.validatesCertificateChain = NO; + } + manager.credential = credential.credential; + manager.responseSerializer = [AFJSONResponseSerializer serializer]; + + // As of 7/29/15 JAMF cloud is returning json data as text/plain + // But add `application/json` to be future ready, for more approperiate + // Content-Types + [manager.responseSerializer setAcceptableContentTypes:[NSSet setWithObjects: + @"application/json", + @"text/plain", nil]]; + + NSString *fullPath = [credential.serverURL.path + stringByAppendingPathComponent:@"JSSResource/distributionpoints"]; + + [manager GET:fullPath parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { + reply(responseObject, nil); + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + reply(nil, error); + }]; +} + - (void)retrieveDistributionPoints:(LGHTTPCredential *)credential - reply:(void (^)(NSDictionary *distributionPoints, NSError *error))reply; + reply:(void (^)(NSDictionary *distributionPoints, NSError *error))reply { - // Setup the request - NSString *distPointAddress = [credential.server stringByAppendingPathComponent:@"JSSResource/distributionpoints"]; + NSMutableString *distPointAddress = [credential.server.trailingSlashRemoved mutableCopy]; + [ distPointAddress appendString:@"/JSSResource/distributionpoints" ]; + NSURL *url = [NSURL URLWithString:distPointAddress]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + [request setValue:@"application/xml" forHTTPHeaderField:@"Accept"]; request.timeoutInterval = 5.0; @@ -62,6 +102,16 @@ - (void)retrieveDistributionPoints:(LGHTTPCredential *)credential operation.securityPolicy = policy; + [operation setWillSendRequestForAuthenticationChallengeBlock:^(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge) { + if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){ + [credential handleCertificateTrustChallenge:challenge reply:^(LGSSLTrustSettings trust) {}]; + } else if (credential.credential && challenge.previousFailureCount < 1) { + [challenge.sender useCredential:credential.credential forAuthenticationChallenge:challenge]; + } else { + [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; + } + }]; + [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { NSError *error = nil; diff --git a/AutoPkgr/Views-Controllers-XIB/Integration Views/LGAutoPkgIntegrationView.m b/AutoPkgr/Views-Controllers-XIB/Integration Views/LGAutoPkgIntegrationView.m index b46b47fb..9ea9d482 100644 --- a/AutoPkgr/Views-Controllers-XIB/Integration Views/LGAutoPkgIntegrationView.m +++ b/AutoPkgr/Views-Controllers-XIB/Integration Views/LGAutoPkgIntegrationView.m @@ -51,22 +51,21 @@ - (void)awakeFromNib if ([LGAutoPkgTask apiTokenFileExists:nil]) { _generateAPITokenBT.title = kLGTokenRevokeButonTitle; _generateAPITokenBT.action = @selector(deleteAPIToken:); + } - LGDefaults *defaults = [LGDefaults standardUserDefaults]; - - BOOL systemProxyCheck = [defaults boolForKey:@"useSystemProxies"]; - NSString *proxyCheck = [defaults objectForKey:@"HTTP_PROXY"]; - NSString *proxyCheck2 = [defaults objectForKey:@"HTTPS_PROXY"]; - - if (systemProxyCheck) { - [self.proxySelectionMatrix selectCellAtRow:1 column:0]; - } else if(proxyCheck || proxyCheck2){ - [self.proxySelectionMatrix selectCellAtRow:2 column:0]; - self.useCustomProxies = YES; - } else { - [self.proxySelectionMatrix selectCellAtRow:0 column:0]; - } + LGDefaults *defaults = [LGDefaults standardUserDefaults]; + BOOL systemProxyCheck = [defaults boolForKey:@"useSystemProxies"]; + NSString *proxyCheck = [defaults objectForKey:@"HTTP_PROXY"]; + NSString *proxyCheck2 = [defaults objectForKey:@"HTTPS_PROXY"]; + + if (systemProxyCheck) { + [self.proxySelectionMatrix selectCellAtRow:1 column:0]; + } else if(proxyCheck || proxyCheck2){ + [self.proxySelectionMatrix selectCellAtRow:2 column:0]; + self.useCustomProxies = YES; + } else { + [self.proxySelectionMatrix selectCellAtRow:0 column:0]; } } diff --git a/AutoPkgr/Views-Controllers-XIB/Integration Views/LGAutoPkgIntegrationView.xib b/AutoPkgr/Views-Controllers-XIB/Integration Views/LGAutoPkgIntegrationView.xib index 732d2763..2edc8a28 100644 --- a/AutoPkgr/Views-Controllers-XIB/Integration Views/LGAutoPkgIntegrationView.xib +++ b/AutoPkgr/Views-Controllers-XIB/Integration Views/LGAutoPkgIntegrationView.xib @@ -49,8 +49,9 @@ - + + http://username:password@proxy:8080 @@ -65,8 +66,9 @@ - + + http://username:password@proxy:8080 @@ -81,8 +83,9 @@ - + + .local,10.0.0.0/24,.mylocaldomain.com @@ -223,6 +226,13 @@ + + + + + + + + @@ -263,6 +265,7 @@ + @@ -289,8 +292,10 @@ + + diff --git a/AutoPkgr/Views-Controllers-XIB/LGConfigurationWindowController.m b/AutoPkgr/Views-Controllers-XIB/LGConfigurationWindowController.m index b7fc0d67..c874f361 100644 --- a/AutoPkgr/Views-Controllers-XIB/LGConfigurationWindowController.m +++ b/AutoPkgr/Views-Controllers-XIB/LGConfigurationWindowController.m @@ -59,7 +59,7 @@ - (instancetype)initWithProgressDelegate:(id)progressDelegat if (self = [self init]) { /* In the main init method, the progress delegate is set as self by default, * but with the installView and schedule view want their progress delegate */ - _scheduleView.progressDelegate = progressDelegate; +// _scheduleView.progressDelegate = progressDelegate; } return self; } diff --git a/AutoPkgr/Views-Controllers-XIB/Notification Views/LGHipChatNotificationView.xib b/AutoPkgr/Views-Controllers-XIB/Notification Views/LGHipChatNotificationView.xib index 42f01038..ac881552 100644 --- a/AutoPkgr/Views-Controllers-XIB/Notification Views/LGHipChatNotificationView.xib +++ b/AutoPkgr/Views-Controllers-XIB/Notification Views/LGHipChatNotificationView.xib @@ -1,5 +1,5 @@ - + diff --git a/AutoPkgr/Views-Controllers-XIB/Notification Views/LGSlackNotificationView.xib b/AutoPkgr/Views-Controllers-XIB/Notification Views/LGSlackNotificationView.xib index 02eafbb1..c984c35a 100644 --- a/AutoPkgr/Views-Controllers-XIB/Notification Views/LGSlackNotificationView.xib +++ b/AutoPkgr/Views-Controllers-XIB/Notification Views/LGSlackNotificationView.xib @@ -1,5 +1,5 @@ - + @@ -14,11 +14,11 @@ - + - + @@ -29,15 +29,15 @@ - + -