iOS web browser with a focus on security and privacy
at master 182 lines 5.3 kB view raw
1/* 2 * Endless 3 * Copyright (c) 2014-2015 joshua stein <jcs@jcs.org> 4 * 5 * See LICENSE file for redistribution terms. 6 */ 7 8#import "HTTPSEverywhere.h" 9#import "HTTPSEverywhereRule.h" 10 11@implementation HTTPSEverywhereRule 12 13/* typical ruleset imported from XML rule: 14 15 ruleset = { 16 exclusion = { 17 pattern = "^http://(help|meme)\\.duckduckgo\\.com/"; 18 }; 19 name = DuckDuckGo; 20 rule = ( 21 { 22 from = "^http://duckduckgo\\.com/"; 23 to = "https://duckduckgo.com/"; 24 }, 25 { 26 from = "^http://([^/:@\\.]+)\\.duckduckgo\\.com/"; 27 to = "https://$1.duckduckgo.com/"; 28 }, 29 ); 30 securecookie = { 31 host = "^duck\\.co$"; 32 name = ".*"; 33 }; 34 target = ( 35 { 36 host = "duckduckgo.com"; 37 }, 38 { 39 host = "*.duckduckgo.com"; 40 }, 41 ); 42 }; 43*/ 44 45- (id)initWithDictionary:(NSDictionary *)dict 46{ 47 NSError *error; 48 NSObject *t; 49 50 NSDictionary *ruleset = [dict objectForKey:@"ruleset"]; 51 if (ruleset == nil) { 52 NSLog(@"[HTTPSEverywhere] ruleset dict not found in %@", dict); 53 return nil; 54 } 55 56 self = [super init]; 57 if (!self) 58 return nil; 59 60 self.name = (NSString *)[ruleset objectForKey:@"name"]; 61 62 NSString *doff = [ruleset objectForKey:@"default_off"]; 63 if (doff != nil && ![doff isEqualToString:@""]) { 64 self.on_by_default = NO; 65 self.notes = doff; 66 } else { 67 self.on_by_default = YES; 68 } 69 70 self.platform = (NSString *)[ruleset objectForKey:@"platform"]; 71 /* TODO: do something useful with platform to disable rules */ 72 73 /* exclusions */ 74 if ((t = [ruleset objectForKey:@"exclusion"]) != nil) { 75 if (![t isKindOfClass:[NSArray class]]) 76 t = [[NSArray alloc] initWithObjects:t, nil]; 77 78 NSMutableArray *excs = [[NSMutableArray alloc] initWithCapacity:2]; 79 80 for (NSDictionary *excd in (NSArray *)t) { 81 NSString *pattern = [excd valueForKey:@"pattern"]; 82 83 NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error]; 84 if (error != nil) { 85 NSLog(@"[HTTPSEverywhere] error compiling regex %@: %@", pattern, error); 86 continue; 87 } 88 89 [excs addObject:regex]; 90 } 91 92 self.exclusions = excs; 93 } 94 95 /* actual url mappings, dictionary of input url regex -> good url */ 96 if ((t = [ruleset objectForKey:@"rule"]) != nil) { 97 if (![t isKindOfClass:[NSArray class]]) 98 t = [[NSArray alloc] initWithObjects:t, nil]; 99 100 NSMutableDictionary *rulesd = [[NSMutableDictionary alloc] initWithCapacity:2]; 101 102 for (NSDictionary *ruled in (NSArray *)t) { 103 NSString *from = [ruled valueForKey:@"from"]; 104 NSString *to = [ruled valueForKey:@"to"]; 105 106 NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:from options:NSRegularExpressionCaseInsensitive error:&error]; 107 if (error != nil) { 108 NSLog(@"[HTTPSEverywhere] error compiling regex %@: %@", from, error); 109 continue; 110 } 111 112 [rulesd setObject:to forKey:regex]; 113 } 114 115 self.rules = rulesd; 116 } 117 118 /* securecookies, dictionary of host regex -> cookie name regex */ 119 if ((t = [ruleset objectForKey:@"securecookie"]) != nil) { 120 if (![t isKindOfClass:[NSArray class]]) 121 t = [[NSArray alloc] initWithObjects:t, nil]; 122 123 NSMutableDictionary *scooksd = [[NSMutableDictionary alloc] initWithCapacity:2]; 124 125 for (NSDictionary *scookd in (NSArray *)t) { 126 NSString *host = [scookd valueForKey:@"host"]; 127 NSString *cname = [scookd valueForKey:@"name"]; 128 129 NSRegularExpression *hostreg = [NSRegularExpression regularExpressionWithPattern:host options:NSRegularExpressionCaseInsensitive error:&error]; 130 if (error != nil) { 131 NSLog(@"[HTTPSEverywhere] error compiling regex %@: %@", host, error); 132 continue; 133 } 134 135 NSRegularExpression *namereg = [NSRegularExpression regularExpressionWithPattern:cname options:NSRegularExpressionCaseInsensitive error:&error]; 136 if (error != nil) { 137 NSLog(@"[HTTPSEverywhere] error compiling regex %@: %@", cname, error); 138 continue; 139 } 140 141 [scooksd setObject:namereg forKey:hostreg]; 142 } 143 144 self.secureCookies = scooksd; 145 } 146 147 return self; 148} 149 150/* return nil if URL was not modified by this rule */ 151- (NSURL *)apply:(NSURL *)url 152{ 153 NSString *absURL = [url absoluteString]; 154 NSArray *matches; 155 156 for (NSRegularExpression *reg in (NSArray *)self.exclusions) { 157 if ((matches = [reg matchesInString:absURL options:0 range:NSMakeRange(0, [absURL length])]) != nil && [matches count] > 0) { 158#ifdef TRACE_HTTPS_EVERYWHERE 159 NSLog(@"[HTTPSEverywhere] [%@] exclusion %@ matched %@", self.name, [reg pattern], absURL); 160#endif 161 return nil; 162 } 163 } 164 165 for (NSRegularExpression *reg in (NSDictionary *)self.rules) { 166 if ((matches = [reg matchesInString:absURL options:0 range:NSMakeRange(0, [absURL length])]) != nil && [matches count] > 0) { 167 NSString *dest = [[self rules] objectForKey:reg]; 168 dest = [reg stringByReplacingMatchesInString:absURL options:0 range:NSMakeRange(0, [absURL length]) withTemplate:dest]; 169 170#ifdef TRACE_HTTPS_EVERYWHERE 171 NSLog(@"[HTTPSEverywhere] [%@] rewrote %@ to %@", self.name, absURL, dest); 172#endif 173 174 /* JS implementation says first matching wins */ 175 return [NSURL URLWithString:dest]; 176 } 177 } 178 179 return nil; 180} 181 182@end