iOS web browser with a focus on security and privacy
at remove_ckhttpconnection 346 lines 13 kB view raw
1/* 2 * Endless 3 * Copyright (c) 2014-2017 joshua stein <jcs@jcs.org> 4 * 5 * See LICENSE file for redistribution terms. 6 */ 7 8#import "AppDelegate.h" 9#import "Bookmark.h" 10#import "HTTPSEverywhere.h" 11#import "URLInterceptor.h" 12 13#import "UIResponder+FirstResponder.h" 14 15@implementation AppDelegate 16{ 17 NSMutableArray *_keyCommands; 18 NSMutableArray *_allKeyBindings; 19 NSArray *_allCommandsAndKeyBindings; 20} 21 22- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions 23{ 24 @try { 25 NSURL *resourceURL = [[NSBundle mainBundle] URLForResource:@"fabric.apikey" withExtension:nil]; 26 if (resourceURL) { 27 NSString *fabricAPIKey = [[NSString stringWithContentsOfURL:resourceURL usedEncoding:nil error:nil] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 28 CrashlyticsKit.delegate = self; 29 [Crashlytics startWithAPIKey:fabricAPIKey]; 30 } else { 31 NSLog(@"[AppDelegate] no fabric.apikey found, not enabling fabric"); 32 } 33 } 34 @catch (NSException *e) { 35 NSLog(@"[AppDelegate] failed setting up fabric: %@", e); 36 } 37 38#ifdef USE_DUMMY_URLINTERCEPTOR 39 [NSURLProtocol registerClass:[DummyURLInterceptor class]]; 40#else 41 [NSURLProtocol registerClass:[URLInterceptor class]]; 42#endif 43 44 self.hstsCache = [HSTSCache retrieve]; 45 self.cookieJar = [[CookieJar alloc] init]; 46 [Bookmark retrieveList]; 47 48 [self initializeDefaults]; 49 50 self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 51 self.window.backgroundColor = [UIColor groupTableViewBackgroundColor]; 52 self.window.rootViewController = [[WebViewController alloc] init]; 53 self.window.rootViewController.restorationIdentifier = @"WebViewController"; 54 55 return YES; 56} 57 58- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 59{ 60 [self.window makeKeyAndVisible]; 61 62 return YES; 63} 64 65- (void)applicationWillResignActive:(UIApplication *)application 66{ 67 [application ignoreSnapshotOnNextApplicationLaunch]; 68 [[self webViewController] viewIsNoLongerVisible]; 69} 70 71- (void)applicationDidEnterBackground:(UIApplication *)application 72{ 73 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; 74 75 if (![self areTesting]) { 76 [HostSettings persist]; 77 [[self hstsCache] persist]; 78 } 79 80 if ([userDefaults boolForKey:@"clear_on_background"]) { 81 [[self webViewController] removeAllTabs]; 82 [[self cookieJar] clearAllNonWhitelistedData]; 83 } 84 else 85 [[self cookieJar] clearAllOldNonWhitelistedData]; 86 87 [application ignoreSnapshotOnNextApplicationLaunch]; 88} 89 90- (void)applicationDidBecomeActive:(UIApplication *)application 91{ 92 [[self webViewController] viewIsVisible]; 93} 94 95- (void)applicationWillTerminate:(UIApplication *)application 96{ 97 /* this definitely ends our sessions */ 98 [[self cookieJar] clearAllNonWhitelistedData]; 99 100 [application ignoreSnapshotOnNextApplicationLaunch]; 101} 102 103- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation 104{ 105#ifdef TRACE 106 NSLog(@"[AppDelegate] request to open url \"%@\"", url); 107#endif 108 if ([[[url scheme] lowercaseString] isEqualToString:@"endlesshttp"]) 109 url = [NSURL URLWithString:[[url absoluteString] stringByReplacingCharactersInRange:NSMakeRange(0, [@"endlesshttp" length]) withString:@"http"]]; 110 else if ([[[url scheme] lowercaseString] isEqualToString:@"endlesshttps"]) 111 url = [NSURL URLWithString:[[url absoluteString] stringByReplacingCharactersInRange:NSMakeRange(0, [@"endlesshttps" length]) withString:@"https"]]; 112 113 [[self webViewController] dismissViewControllerAnimated:YES completion:nil]; 114 [[self webViewController] addNewTabForURL:url]; 115 116 return YES; 117} 118 119- (BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder 120{ 121 if ([self areTesting]) 122 return NO; 123 124 /* if we tried last time and failed, the state might be corrupt */ 125 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; 126 if ([userDefaults objectForKey:STATE_RESTORE_TRY_KEY] != nil) { 127 NSLog(@"[AppDelegate] previous startup failed, not restoring application state"); 128 [userDefaults removeObjectForKey:STATE_RESTORE_TRY_KEY]; 129 return NO; 130 } 131 else 132 [userDefaults setBool:YES forKey:STATE_RESTORE_TRY_KEY]; 133 134 [userDefaults synchronize]; 135 136 return YES; 137} 138 139- (BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder 140{ 141 if ([self areTesting]) 142 return NO; 143 144 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; 145 if ([userDefaults boolForKey:@"clear_on_background"]) 146 return NO; 147 148 return YES; 149} 150 151- (void)crashlyticsDidDetectReportForLastExecution:(CLSReport *)report completionHandler:(void (^)(BOOL))completionHandler 152{ 153 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; 154 155#ifdef TRACE 156 NSLog(@"crashlytics report found, %@sending to crashlytics: %@", ([userDefaults boolForKey:@"crash_reporting"] ? @"" : @"NOT "), report); 157#endif 158 159 completionHandler([userDefaults boolForKey:@"crash_reporting"]); 160} 161 162- (NSArray<UIKeyCommand *> *)keyCommands 163{ 164 if (!_keyCommands) { 165 _keyCommands = [[NSMutableArray alloc] init]; 166 167 [_keyCommands addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:UIKeyModifierCommand action:@selector(handleKeyboardShortcut:) discoverabilityTitle:@"Go Back"]]; 168 [_keyCommands addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:UIKeyModifierCommand action:@selector(handleKeyboardShortcut:) discoverabilityTitle:@"Go Forward"]]; 169 170 [_keyCommands addObject:[UIKeyCommand keyCommandWithInput:@"b" modifierFlags:UIKeyModifierCommand action:@selector(handleKeyboardShortcut:) discoverabilityTitle:@"Show Bookmarks"]]; 171 172 [_keyCommands addObject:[UIKeyCommand keyCommandWithInput:@"l" modifierFlags:UIKeyModifierCommand action:@selector(handleKeyboardShortcut:) discoverabilityTitle:@"Focus URL Field"]]; 173 174 [_keyCommands addObject:[UIKeyCommand keyCommandWithInput:@"t" modifierFlags:UIKeyModifierCommand action:@selector(handleKeyboardShortcut:) discoverabilityTitle:@"Create New Tab"]]; 175 [_keyCommands addObject:[UIKeyCommand keyCommandWithInput:@"w" modifierFlags:UIKeyModifierCommand action:@selector(handleKeyboardShortcut:) discoverabilityTitle:@"Close Tab"]]; 176 177 for (int i = 1; i <= 10; i++) 178 [_keyCommands addObject:[UIKeyCommand keyCommandWithInput:[NSString stringWithFormat:@"%d", (i == 10 ? 0 : i)] modifierFlags:UIKeyModifierCommand action:@selector(handleKeyboardShortcut:) discoverabilityTitle:[NSString stringWithFormat:@"Switch to Tab %d", i]]]; 179 } 180 181 if (!_allKeyBindings) { 182 _allKeyBindings = [[NSMutableArray alloc] init]; 183 const long modPermutations[] = { 184 UIKeyModifierAlphaShift, 185 UIKeyModifierShift, 186 UIKeyModifierControl, 187 UIKeyModifierAlternate, 188 UIKeyModifierCommand, 189 UIKeyModifierCommand | UIKeyModifierAlternate, 190 UIKeyModifierCommand | UIKeyModifierControl, 191 UIKeyModifierControl | UIKeyModifierAlternate, 192 UIKeyModifierControl | UIKeyModifierCommand, 193 UIKeyModifierControl | UIKeyModifierAlternate | UIKeyModifierCommand, 194 kNilOptions, 195 }; 196 197 NSString *chars = @"`1234567890-=\b\tqwertyuiop[]\\asdfghjkl;'\rzxcvbnm,./ "; 198 for (int j = 0; j < sizeof(modPermutations); j++) { 199 for (int i = 0; i < [chars length]; i++) { 200 NSString *c = [chars substringWithRange:NSMakeRange(i, 1)]; 201 202 [_allKeyBindings addObject:[UIKeyCommand keyCommandWithInput:c modifierFlags:modPermutations[j] action:@selector(handleKeyboardShortcut:)]]; 203 } 204 205 [_allKeyBindings addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:modPermutations[j] action:@selector(handleKeyboardShortcut:)]]; 206 [_allKeyBindings addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:modPermutations[j] action:@selector(handleKeyboardShortcut:)]]; 207 [_allKeyBindings addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:modPermutations[j] action:@selector(handleKeyboardShortcut:)]]; 208 [_allKeyBindings addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:modPermutations[j] action:@selector(handleKeyboardShortcut:)]]; 209 [_allKeyBindings addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputEscape modifierFlags:modPermutations[j] action:@selector(handleKeyboardShortcut:)]]; 210 } 211 212 _allCommandsAndKeyBindings = [_keyCommands arrayByAddingObjectsFromArray:_allKeyBindings]; 213 } 214 215 /* if settings are up or something else, ignore shortcuts */ 216 if (![[self topViewController] isKindOfClass:[WebViewController class]]) 217 return nil; 218 219 id cur = [UIResponder currentFirstResponder]; 220 if (cur == nil || [NSStringFromClass([cur class]) isEqualToString:@"UIWebView"]) 221 return _allCommandsAndKeyBindings; 222 else { 223#ifdef TRACE_KEYBOARD_INPUT 224 NSLog(@"[AppDelegate] current first responder is a %@, only passing shortcuts", NSStringFromClass([cur class])); 225#endif 226 return _keyCommands; 227 } 228} 229 230- (void)handleKeyboardShortcut:(UIKeyCommand *)keyCommand 231{ 232 if ([keyCommand modifierFlags] == UIKeyModifierCommand) { 233 if ([[keyCommand input] isEqualToString:@"b"]) { 234 [[self webViewController] showBookmarksForEditing:NO]; 235 return; 236 } 237 238 if ([[keyCommand input] isEqualToString:@"l"]) { 239 [[self webViewController] focusUrlField]; 240 return; 241 } 242 243 if ([[keyCommand input] isEqualToString:@"t"]) { 244 [[self webViewController] addNewTabForURL:nil forRestoration:NO withCompletionBlock:^(BOOL finished) { 245 [[self webViewController] focusUrlField]; 246 }]; 247 return; 248 } 249 250 if ([[keyCommand input] isEqualToString:@"w"]) { 251 [[self webViewController] removeTab:[[[self webViewController] curWebViewTab] tabIndex]]; 252 return; 253 } 254 255 if ([[keyCommand input] isEqualToString:UIKeyInputLeftArrow]) { 256 [[[self webViewController] curWebViewTab] goBack]; 257 return; 258 } 259 260 if ([[keyCommand input] isEqualToString:UIKeyInputRightArrow]) { 261 [[[self webViewController] curWebViewTab] goForward]; 262 return; 263 } 264 265 for (int i = 0; i <= 9; i++) { 266 if ([[keyCommand input] isEqualToString:[NSString stringWithFormat:@"%d", i]]) { 267 [[self webViewController] switchToTab:[NSNumber numberWithInt:(i == 0 ? 9 : i - 1)]]; 268 return; 269 } 270 } 271 } 272 273 if ([self webViewController] && [[self webViewController] curWebViewTab]) 274 [[[self webViewController] curWebViewTab] handleKeyCommand:keyCommand]; 275} 276 277- (UIViewController *)topViewController 278{ 279 return [self topViewController:[UIApplication sharedApplication].keyWindow.rootViewController]; 280} 281 282- (UIViewController *)topViewController:(UIViewController *)rootViewController 283{ 284 if (rootViewController.presentedViewController == nil) 285 return rootViewController; 286 287 if ([rootViewController.presentedViewController isMemberOfClass:[UINavigationController class]]) { 288 UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController; 289 UIViewController *lastViewController = [[navigationController viewControllers] lastObject]; 290 return [self topViewController:lastViewController]; 291 } 292 293 UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController; 294 return [self topViewController:presentedViewController]; 295} 296 297- (void)initializeDefaults 298{ 299 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; 300 301 NSString *plistPath = [[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"InAppSettings.bundle"] stringByAppendingPathComponent:@"Root.inApp.plist"]; 302 NSDictionary *settingsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath]; 303 304 for (NSDictionary *pref in [settingsDictionary objectForKey:@"PreferenceSpecifiers"]) { 305 NSString *key = [pref objectForKey:@"Key"]; 306 if (key == nil) 307 continue; 308 309 if ([userDefaults objectForKey:key] == NULL) { 310 NSObject *val = [pref objectForKey:@"DefaultValue"]; 311 if (val == nil) 312 continue; 313 314 [userDefaults setObject:val forKey:key]; 315#ifdef TRACE 316 NSLog(@"[AppDelegate] initialized default preference for %@ to %@", key, val); 317#endif 318 } 319 } 320 321 if (![userDefaults synchronize]) { 322 NSLog(@"[AppDelegate] failed saving preferences"); 323 abort(); 324 } 325 326 _searchEngines = [NSMutableDictionary dictionaryWithContentsOfFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"SearchEngines.plist"]]; 327} 328 329- (BOOL)areTesting 330{ 331 if (NSClassFromString(@"XCTestProbe") != nil) { 332 NSLog(@"we are testing"); 333 return YES; 334 } 335 else { 336 NSDictionary *environment = [[NSProcessInfo processInfo] environment]; 337 if (environment[@"ARE_UI_TESTING"]) { 338 NSLog(@"we are UI testing"); 339 return YES; 340 } 341 } 342 343 return NO; 344} 345 346@end