View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -3,8 +3,6 @@
#import "WebViewController.h"
#define TRACE
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@@ -19,6 +17,7 @@
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
- (void)dumpCookies;
@end
View
@@ -123,4 +123,11 @@ - (void)saveContext {
}
}
- (void)dumpCookies {
NSLog(@"cookie dump:");
for (NSHTTPCookie *cookie in [[self cookieStorage] cookies]) {
NSLog(@" %@: \"%@\"=\"%@\"", cookie.domain, cookie.name, cookie.value);
}
}
@end
View
@@ -43,11 +43,11 @@
<barButtonItem style="plain" systemItem="flexibleSpace" id="ub5-GA-Wkr"/>
<barButtonItem image="forward" style="plain" id="N4X-8l-n5e"/>
<barButtonItem style="plain" systemItem="flexibleSpace" id="eUB-No-f1V"/>
<barButtonItem style="plain" systemItem="action" id="keA-rM-8YW"/>
<barButtonItem style="plain" systemItem="flexibleSpace" id="Nr1-t0-Y8j"/>
<barButtonItem style="plain" systemItem="bookmarks" id="rmK-pa-uwX">
<inset key="imageInsets" minX="0.0" minY="0.0" maxX="0.0" maxY="-4"/>
<barButtonItem style="plain" systemItem="action" id="keA-rM-8YW">
<inset key="imageInsets" minX="0.0" minY="-1" maxX="0.0" maxY="1"/>
</barButtonItem>
<barButtonItem style="plain" systemItem="flexibleSpace" id="Nr1-t0-Y8j"/>
<barButtonItem style="plain" systemItem="bookmarks" id="rmK-pa-uwX"/>
<barButtonItem style="plain" systemItem="flexibleSpace" id="Kex-1H-tJL"/>
<barButtonItem style="plain" systemItem="organize" id="RXJ-Te-n3L"/>
</items>
View
@@ -0,0 +1,13 @@
#import <Foundation/Foundation.h>
#import "HTTPSEverywhereRule.h"
@interface HTTPSEverywhere : NSObject
+ (NSDictionary *)rules;
+ (NSDictionary *)targets;
+ (HTTPSEverywhereRule *)cachedRuleForName:(NSString *)name;
+ (NSArray *)potentiallyApplicableRulesFor:(NSString *)host;
+ (NSURL *)rewrittenURI:(NSURL *)URL;
@end
View
@@ -0,0 +1,128 @@
#import "HTTPSEverywhere.h"
@implementation HTTPSEverywhere
static NSDictionary *_rules;
static NSDictionary *_targets;
static NSCache *ruleCache;
#define RULE_CACHE_SIZE 20
+ (NSDictionary *)rules {
if (_rules == nil) {
NSFileManager *fm = [NSFileManager defaultManager];
NSString *path = [[NSBundle mainBundle] pathForResource:@"https-everywhere_rules" ofType:@"plist"];
if (![fm fileExistsAtPath:path]) {
NSLog(@"[HTTPSEverywhere] no rule plist at %@", path);
abort();
}
_rules = [NSDictionary dictionaryWithContentsOfFile:path];
#ifdef TRACE_HTTPS_EVERYWHERE
NSLog(@"[HTTPSEverywhere] locked and loaded with %lu rules", [_rules count]);
#endif
}
return _rules;
}
+ (NSDictionary *)targets {
if (_targets == nil) {
NSFileManager *fm = [NSFileManager defaultManager];
NSString *path = [[NSBundle mainBundle] pathForResource:@"https-everywhere_targets" ofType:@"plist"];
if (![fm fileExistsAtPath:path]) {
NSLog(@"[HTTPSEverywhere] no target plist at %@", path);
abort();
}
_targets = [NSDictionary dictionaryWithContentsOfFile:path];
#ifdef TRACE_HTTPS_EVERYWHERE
NSLog(@"[HTTPSEverywhere] locked and loaded with %lu target domains", [_targets count]);
#endif
}
return _targets;
}
+ (void)cacheRule:(HTTPSEverywhereRule *)rule forName:(NSString *)name {
if (!ruleCache) {
ruleCache = [[NSCache alloc] init];
[ruleCache setCountLimit:RULE_CACHE_SIZE];
}
#ifdef TRACE_HTTPS_EVERYWHERE
NSLog(@"[HTTPSEverywhere] cache miss for %@", name);
#endif
[ruleCache setObject:rule forKey:name];
}
+ (HTTPSEverywhereRule *)cachedRuleForName:(NSString *)name {
HTTPSEverywhereRule *r;
if (ruleCache) {
if ((r = [ruleCache objectForKey:name]) != nil) {
#ifdef TRACE_HTTPS_EVERYWHERE
NSLog(@"[HTTPSEverywhere] cache hit for %@", name);
#endif
return r;
}
}
r = [[HTTPSEverywhereRule alloc] initWithDictionary:[[[self class] rules] objectForKey:name]];
[[self class] cacheRule:r forName:name];
return r;
}
+ (NSArray *)potentiallyApplicableRulesFor:(NSString *)host {
NSMutableDictionary *rs = [[NSMutableDictionary alloc] initWithCapacity:2];
host = [host lowercaseString];
NSString *targetName = [[[self class] targets] objectForKey:host];
if (targetName != nil)
[rs setValue:[[self class] cachedRuleForName:targetName] forKey:targetName];
/* now for x.y.z.example.com, try *.y.z.example.com, *.z.example.com, *.example.com, etc. */
/* TODO: should we skip the last component for obviously non-matching things like "*.com", "*.net"? */
NSArray *hostp = [host componentsSeparatedByString:@"."];
for (int i = 1; i < [hostp count]; i++) {
NSString *wc = [[hostp subarrayWithRange:NSMakeRange(i, [hostp count] - i)] componentsJoinedByString:@"."];
NSString *targetName = [[[self class] targets] objectForKey:wc];
if (targetName != nil) {
#ifdef TRACE_HTTPS_EVERYWHERE
NSLog(@"[HTTPSEverywhere] found ruleset %@ for component %@ in %@", targetName, wc, host);
#endif
if (![rs objectForKey:targetName])
[rs setValue:[[self class] cachedRuleForName:targetName] forKey:targetName];
}
}
return [rs allValues];
}
+ (NSURL *)rewrittenURI:(NSURL *)URL {
NSArray *rs = [[self class] potentiallyApplicableRulesFor:[URL host]];
if (rs == nil || [rs count] == 0)
return URL;
#ifdef TRACE_HTTPS_EVERYWHERE
NSLog(@"[HTTPSEverywhere] have %lu applicable ruleset(s) for %@", [rs count], [URL absoluteString]);
#endif
for (HTTPSEverywhereRule *rule in rs) {
NSURL *rurl = [rule apply:URL];
if (rurl != nil)
return rurl;
}
return URL;
}
@end
View
@@ -0,0 +1,18 @@
#import <Foundation/Foundation.h>
@interface HTTPSEverywhereRule : NSObject
@property NSString *name;
@property NSArray *exclusions;
@property NSDictionary *rules;
@property NSDictionary *securecookies;
@property NSString *platform;
@property BOOL on_by_default;
@property NSString *notes;
/* not loaded here since HTTPSEverywhere class has a big list of them */
@property NSArray *targets;
- (id)initWithDictionary:(NSDictionary *)dict;
- (NSURL *)apply:(NSURL *)url;
@end
View
@@ -0,0 +1,172 @@
#import "HTTPSEverywhereRule.h"
@implementation HTTPSEverywhereRule
/* typical ruleset imported from XML rule:
ruleset = {
exclusion = {
pattern = "^http://(help|meme)\\.duckduckgo\\.com/";
};
name = DuckDuckGo;
rule = (
{
from = "^http://duckduckgo\\.com/";
to = "https://duckduckgo.com/";
},
{
from = "^http://([^/:@\\.]+)\\.duckduckgo\\.com/";
to = "https://$1.duckduckgo.com/";
},
);
securecookie = {
host = "^duck\\.co$";
name = ".*";
};
target = (
{
host = "duckduckgo.com";
},
{
host = "*.duckduckgo.com";
},
);
};
*/
- (id)initWithDictionary:(NSDictionary *)dict {
NSError *error;
NSObject *t;
NSDictionary *ruleset = [dict objectForKey:@"ruleset"];
if (ruleset == nil) {
NSLog(@"[HTTPSEverywhere] ruleset dict not found in %@", dict);
return nil;
}
self.name = (NSString *)[ruleset objectForKey:@"name"];
NSString *doff = [ruleset objectForKey:@"default_off"];
if (doff != nil && ![doff isEqualToString:@""]) {
self.on_by_default = NO;
self.notes = doff;
} else {
self.on_by_default = YES;
}
self.platform = (NSString *)[ruleset objectForKey:@"platform"];
/* TODO: do something useful with platform to disable rules */
/* exclusions */
if ((t = [ruleset objectForKey:@"exclusion"]) != nil) {
if (![t isKindOfClass:[NSArray class]])
t = [[NSArray alloc] initWithObjects:t, nil];
NSMutableArray *excs = [[NSMutableArray alloc] initWithCapacity:2];
for (NSDictionary *excd in (NSArray *)t) {
NSString *pattern = [excd valueForKey:@"pattern"];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];
if (error != nil) {
NSLog(@"[HTTPSEverywhere] error compiling regex %@: %@", pattern, error);
continue;
}
[excs addObject:regex];
}
self.exclusions = excs;
}
/* actual url mappings, dictionary of input url regex -> good url */
if ((t = [ruleset objectForKey:@"rule"]) != nil) {
if (![t isKindOfClass:[NSArray class]])
t = [[NSArray alloc] initWithObjects:t, nil];
NSMutableDictionary *rulesd = [[NSMutableDictionary alloc] initWithCapacity:2];
for (NSDictionary *ruled in (NSArray *)t) {
NSString *from = [ruled valueForKey:@"from"];
NSString *to = [ruled valueForKey:@"to"];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:from options:NSRegularExpressionCaseInsensitive error:&error];
if (error != nil) {
NSLog(@"[HTTPSEverywhere] error compiling regex %@: %@", from, error);
continue;
}
[rulesd setObject:to forKey:regex];
}
self.rules = rulesd;
}
/* securecookies, dictionary of host regex -> cookie name regex */
if ((t = [ruleset objectForKey:@"securecookie"]) != nil) {
if (![t isKindOfClass:[NSArray class]])
t = [[NSArray alloc] initWithObjects:t, nil];
NSMutableDictionary *scooksd = [[NSMutableDictionary alloc] initWithCapacity:2];
for (NSDictionary *scookd in (NSArray *)t) {
NSString *host = [scookd valueForKey:@"host"];
NSString *cname = [scookd valueForKey:@"name"];
NSRegularExpression *hostreg = [NSRegularExpression regularExpressionWithPattern:host options:NSRegularExpressionCaseInsensitive error:&error];
if (error != nil) {
NSLog(@"[HTTPSEverywhere] error compiling regex %@: %@", host, error);
continue;
}
NSRegularExpression *namereg = [NSRegularExpression regularExpressionWithPattern:cname options:NSRegularExpressionCaseInsensitive error:&error];
if (error != nil) {
NSLog(@"[HTTPSEverywhere] error compiling regex %@: %@", cname, error);
continue;
}
[scooksd setObject:namereg forKey:hostreg];
}
self.securecookies = scooksd;
}
#ifdef TRACE_HTTPS_EVERYWHERE
NSLog(@"[HTTPSEverywhere] parsed plist for %@", self.name);
#endif
return self;
}
/* return nil if URL was not modified by this rule */
- (NSURL *)apply:(NSURL *)url {
NSString *absURL = [url absoluteString];
NSArray *matches;
for (NSRegularExpression *reg in (NSArray *)self.exclusions) {
if ((matches = [reg matchesInString:absURL options:0 range:NSMakeRange(0, [absURL length])]) != nil && [matches count] > 0) {
#ifdef TRACE_HTTPS_EVERYWHERE
NSLog(@"[HTTPSEverywhere] [%@] exclusion %@ matched %@", self.name, [reg pattern], absURL);
#endif
return nil;
}
}
for (NSRegularExpression *reg in (NSDictionary *)self.rules) {
if ((matches = [reg matchesInString:absURL options:0 range:NSMakeRange(0, [absURL length])]) != nil && [matches count] > 0) {
NSString *dest = [[self rules] objectForKey:reg];
dest = [reg stringByReplacingMatchesInString:absURL options:0 range:NSMakeRange(0, [absURL length]) withTemplate:dest];
#ifdef TRACE_HTTPS_EVERYWHERE
NSLog(@"[HTTPSEverywhere] [%@] rewrote %@ to %@", self.name, absURL, dest);
#endif
/* JS implementation says first matching wins */
return [NSURL URLWithString:dest];
}
}
return nil;
}
@end
View
@@ -1,4 +1,5 @@
#import "AppDelegate.h"
#import "HTTPSEverywhere.h"
#import "URLInterceptor.h"
@interface URLInterceptor ()
@@ -15,6 +16,10 @@ + (BOOL)canInitWithRequest:(NSURLRequest *)request {
if ([NSURLProtocol propertyForKey:REWRITTEN_KEY inRequest:request] != nil)
/* already mucked with this request */
return NO;
if ([[[[request URL] scheme] lowercaseString] isEqualToString:@"data"])
/* can't really do anything for these URLs */
return NO;
if (appDelegate == nil)
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
@@ -31,25 +36,37 @@ - (void)startLoading
{
self.origRequest = self.request;
self.evOrgName = nil;
NSMutableURLRequest *newRequest = [self.request mutableCopy];
#ifdef TRACE
NSLog(@"startLoading URL: %@", [[newRequest URL] absoluteString]);
NSLog(@"startLoading URL (%@): %@", [newRequest HTTPMethod], [[newRequest URL] absoluteString]);
#endif
[newRequest setURL:[HTTPSEverywhere rewrittenURI:[[self request] URL]]];
/* redirections can happen without us seeing them, so keep the webview chrome in the loop */
[[appDelegate curWebView] setCurURL:[newRequest mainDocumentURL]];
/* we're handling cookies ourself */
[newRequest setHTTPShouldHandleCookies:NO];
NSArray *cookies = [[appDelegate cookieStorage] cookiesForURL:[newRequest URL]];
if (cookies != nil && [cookies count] > 0) {
#ifdef TRACE
NSLog(@"sending %lu cookie(s) to %@", [cookies count], [newRequest URL]);
#endif
NSDictionary *headers = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
[newRequest setAllHTTPHeaderFields:headers];
}
/* add "do not track" header */
if (true /* TODO: move this to a pref check */) {
[newRequest setValue:@"1" forHTTPHeaderField:@"DNT"];
}
/* remember that we saw this to avoid a loop */
[NSURLProtocol setProperty:@YES forKey:REWRITTEN_KEY inRequest:newRequest];
/* we're handling cookies ourself */
[newRequest setHTTPShouldHandleCookies:NO];
self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
}
@@ -64,16 +81,20 @@ - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSUR
/* we don't get a redirect response when only upgrading from http -> https on the same hostname, so we have to find it ourselves :( */
if (response == nil && [self.origRequest hash] != [request hash]) {
#ifdef TRACE
NSLog(@"hash changed, URL went from %@ to %@", [[self.origRequest URL] absoluteString], [[request URL] absoluteString]);
#endif
schemaRedirect = true;
}
if (response == nil && !schemaRedirect) {
#ifdef TRACE
NSLog(@"willSendRequest to %@", [[request URL] absoluteString]);
NSLog(@"willSendRequest %@ to %@", [request HTTPMethod], [[request URL] absoluteString]);
#endif
}
else {
[self extractCookiesFromResponse:response forURL:[request URL] fromMainDocument:[[appDelegate curWebView] curURL]];
#ifdef TRACE
if (schemaRedirect && response == nil)
NSLog(@"willSendRequest being redirected (schema-only) from %@ to %@", [[self.origRequest URL] absoluteString], [[request URL] absoluteString]);
@@ -98,28 +119,8 @@ - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSUR
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSMutableArray *cookies = [[NSMutableArray alloc] initWithCapacity:5];
for (NSHTTPCookie *cookie in [NSHTTPCookie cookiesWithResponseHeaderFields:[httpResponse allHeaderFields] forURL:[self.request URL]]) {
/* toggle "secure" bit */
NSMutableDictionary *ps = (NSMutableDictionary *)[cookie properties];
[ps setValue:@"TRUE" forKey:NSHTTPCookieSecure];
[self extractCookiesFromResponse:response forURL:[self.request URL] fromMainDocument:[[appDelegate curWebView] curURL]];
/* and make everything a session cookie */
[ps setValue:@"TRUE" forKey:NSHTTPCookieDiscard];
NSHTTPCookie *nCookie = [[NSHTTPCookie alloc] initWithProperties:ps];
[cookies addObject:nCookie];
}
if ([cookies count] > 0) {
#ifdef TRACE
NSLog(@"setting %lu cookie(s) for %@ (via %@)", [cookies count], [[self.request URL] host], [[appDelegate curWebView] curURL]);
#endif
[[appDelegate cookieStorage] setCookies:cookies forURL:[self.request URL] mainDocumentURL:[[appDelegate curWebView] curURL]];
}
if (self.evOrgName != nil && ![self.evOrgName isEqualToString:@""]) {
NSLog(@"EV info is %@", self.evOrgName);
}
@@ -179,4 +180,28 @@ - (void) connection:(NSURLConnection *)conn didReceiveAuthenticationChallenge:(N
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
}
- (void)extractCookiesFromResponse:(NSURLResponse *)response forURL:(NSURL *)url fromMainDocument:(NSURL *)mainDocument {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSMutableArray *cookies = [[NSMutableArray alloc] initWithCapacity:5];
for (NSHTTPCookie *cookie in [NSHTTPCookie cookiesWithResponseHeaderFields:[httpResponse allHeaderFields] forURL:url]) {
/* toggle "secure" bit */
NSMutableDictionary *ps = (NSMutableDictionary *)[cookie properties];
[ps setValue:@"TRUE" forKey:NSHTTPCookieSecure];
/* and make everything a session cookie */
[ps setValue:@"TRUE" forKey:NSHTTPCookieDiscard];
NSHTTPCookie *nCookie = [[NSHTTPCookie alloc] initWithProperties:ps];
[cookies addObject:nCookie];
}
if ([cookies count] > 0) {
#ifdef TRACE
NSLog(@"storing %lu cookie(s) for %@ (via %@)", [cookies count], [url host], mainDocument);
#endif
[[appDelegate cookieStorage] setCookies:cookies forURL:url mainDocumentURL:mainDocument];
}
}
@end
View
@@ -187,15 +187,9 @@ - (void)webViewDidFinishLoad:(UIWebView *)_webView {
#endif
[self updateSearchBarDetails];
#ifdef DEBUG
NSLog(@"cookie dump:");
#endif
for (NSHTTPCookie *cookie in [[appDelegate cookieStorage] cookies]) {
#ifdef DEBUG
NSLog(@" %@: \"%@\"=\"%@\"", cookie.domain, cookie.name, cookie.value);
#ifdef TRACE
[appDelegate dumpCookies];
#endif
[[appDelegate cookieStorage] deleteCookie:cookie];
}
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
View
@@ -0,0 +1,14 @@
#ifndef endless_endless_Prefix_pch
#define endless_endless_Prefix_pch
#ifdef DEBUG
/* NSLog() all network activity and boring stuff */
//# define TRACE
/* be verbose about HTTPS Everywhere workings */
//# define TRACE_HTTPS_EVERYWHERE
#endif
#endif
View
@@ -1,8 +1,6 @@
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#undef TRACE
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
Submodule https-everywhere added at 7876b0
View

Large diffs are not rendered by default.

Oops, something went wrong.
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -0,0 +1,48 @@
#!/usr/bin/ruby
#
# convert all HTTPS Everywhere XML rule files into one big rules hash and write
# it out as a plist, as well as a standalone hash of target URLs -> rule names
# to another plist
#
require "active_support/core_ext/hash/conversions"
require "plist"
rules = {}
targets = {}
Dir.glob(File.dirname(__FILE__) +
"/https-everywhere/src/chrome/content/rules/*.xml").each do |f|
hash = Hash.from_xml(File.read(f))
raise "no ruleset" if !hash["ruleset"]
if hash["ruleset"]["default_off"]
next # XXX: should we store these?
end
raise "conflict on #{f}" if rules[hash["ruleset"]["name"]]
rules[hash["ruleset"]["name"]] = hash
hash["ruleset"]["target"].each do |target|
if !target.is_a?(Hash)
# why do some of these get converted into an array?
if target.length != 2 || target[0] != "host"
puts f
raise target.inspect
end
target = { target[0] => target[1] }
end
if targets[target["host"][1]]
raise "rules already exist for #{target["host"]}"
end
targets[target["host"]] = hash["ruleset"]["name"]
end
end
File.write("https-everywhere_targets.plist", targets.to_plist)
File.write("https-everywhere_rules.plist", rules.to_plist)