iOS web browser with a focus on security and privacy

Add URLBlocker rule controller, allow user to disable rules

Don't bother converting JSON to plist for blocked hosts, just parse
the JSON at startup since it's not very big

+215 -292
+16 -6
Endless.xcodeproj/project.pbxproj
··· 39 39 01AFEB4F1B4F2A5F00A02482 /* OnePasswordExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 01AFEB4D1B4F2A5F00A02482 /* OnePasswordExtension.m */; }; 40 40 01B834921E12DC8D0050EAB5 /* DonationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 01B834901E12DC8D0050EAB5 /* DonationViewController.m */; }; 41 41 01B834951E12F4E70050EAB5 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 01B834941E12F4E70050EAB5 /* StoreKit.framework */; }; 42 + 01BFEE431E3BD6E60069AC83 /* URLBlockerRuleController.m in Sources */ = {isa = PBXBuildFile; fileRef = 01BFEE421E3BD6E60069AC83 /* URLBlockerRuleController.m */; }; 43 + 01BFEE461E3BE1820069AC83 /* RuleEditorRow.m in Sources */ = {isa = PBXBuildFile; fileRef = 01BFEE451E3BE1820069AC83 /* RuleEditorRow.m */; }; 44 + 01BFEE4A1E3D3CD60069AC83 /* urlblocker.json in Resources */ = {isa = PBXBuildFile; fileRef = 01BFEE491E3D3CD60069AC83 /* urlblocker.json */; }; 42 45 01D42C3D1E0A4FE400566022 /* URLInterceptor_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01D42C3C1E0A4FE400566022 /* URLInterceptor_Tests.m */; }; 43 46 01D741281A44DF1C007B7033 /* WebViewMenuController.m in Sources */ = {isa = PBXBuildFile; fileRef = 01D741271A44DF1C007B7033 /* WebViewMenuController.m */; }; 44 47 01D7412A1A45EDD1007B7033 /* CookieJar_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01D741291A45EDD1007B7033 /* CookieJar_Tests.m */; }; ··· 66 69 01F879491A41141800A63654 /* urlblocker_mock_targets.plist in Resources */ = {isa = PBXBuildFile; fileRef = 01F879471A41141800A63654 /* urlblocker_mock_targets.plist */; }; 67 70 01F8794B1A41232E00A63654 /* credits.html in Resources */ = {isa = PBXBuildFile; fileRef = 01F8794A1A41232E00A63654 /* credits.html */; }; 68 71 01F8794C1A4124FE00A63654 /* credits.html in Resources */ = {isa = PBXBuildFile; fileRef = 01F8794A1A41232E00A63654 /* credits.html */; }; 69 - 01F8794E1A412F8E00A63654 /* urlblocker_targets.plist in Resources */ = {isa = PBXBuildFile; fileRef = 01F8794D1A412F8E00A63654 /* urlblocker_targets.plist */; }; 70 - 01F8794F1A412FA500A63654 /* urlblocker_targets.plist in Resources */ = {isa = PBXBuildFile; fileRef = 01F8794D1A412F8E00A63654 /* urlblocker_targets.plist */; }; 71 72 01FC0E571B38FB6B00955D9A /* Launch Screen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 01FC0E561B38FB6B00955D9A /* Launch Screen.xib */; }; 72 73 AA9A5FECA2E668F834FDECD9 /* libPods-Endless.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 095BC7BD685DE17FADDBF166 /* libPods-Endless.a */; }; 73 74 D919EA71FF999D55CCC96334 /* libPods-Endless Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FC10255E5E506A0C25D6276B /* libPods-Endless Tests.a */; }; ··· 140 141 01B8348F1E12DC8D0050EAB5 /* DonationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DonationViewController.h; path = Endless/DonationViewController.h; sourceTree = "<group>"; }; 141 142 01B834901E12DC8D0050EAB5 /* DonationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DonationViewController.m; path = Endless/DonationViewController.m; sourceTree = "<group>"; }; 142 143 01B834941E12F4E70050EAB5 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; 144 + 01BFEE411E3BD6E60069AC83 /* URLBlockerRuleController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = URLBlockerRuleController.h; sourceTree = "<group>"; }; 145 + 01BFEE421E3BD6E60069AC83 /* URLBlockerRuleController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = URLBlockerRuleController.m; sourceTree = "<group>"; }; 146 + 01BFEE441E3BE1820069AC83 /* RuleEditorRow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RuleEditorRow.h; sourceTree = "<group>"; }; 147 + 01BFEE451E3BE1820069AC83 /* RuleEditorRow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RuleEditorRow.m; sourceTree = "<group>"; }; 148 + 01BFEE491E3D3CD60069AC83 /* urlblocker.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = urlblocker.json; path = Endless/Resources/urlblocker.json; sourceTree = "<group>"; }; 143 149 01D42C3B1E0A37DB00566022 /* Endless.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Endless.entitlements; sourceTree = "<group>"; }; 144 150 01D42C3C1E0A4FE400566022 /* URLInterceptor_Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = URLInterceptor_Tests.m; sourceTree = "<group>"; }; 145 151 01D741261A44DF1C007B7033 /* WebViewMenuController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewMenuController.h; sourceTree = "<group>"; }; ··· 176 182 01F879461A41141800A63654 /* urlblocker_mock_rules.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = urlblocker_mock_rules.plist; sourceTree = "<group>"; }; 177 183 01F879471A41141800A63654 /* urlblocker_mock_targets.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = urlblocker_mock_targets.plist; sourceTree = "<group>"; }; 178 184 01F8794A1A41232E00A63654 /* credits.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = credits.html; path = Endless/Resources/credits.html; sourceTree = "<group>"; }; 179 - 01F8794D1A412F8E00A63654 /* urlblocker_targets.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = urlblocker_targets.plist; path = Endless/Resources/urlblocker_targets.plist; sourceTree = "<group>"; }; 180 185 01FC0E561B38FB6B00955D9A /* Launch Screen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = "Launch Screen.xib"; path = "Endless/Resources/Launch Screen.xib"; sourceTree = "<group>"; }; 181 186 095BC7BD685DE17FADDBF166 /* libPods-Endless.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Endless.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 182 187 5BC57F74FB0E2BC3CDAB5D1D /* Pods-Endless Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Endless Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Endless Tests/Pods-Endless Tests.release.xcconfig"; sourceTree = "<group>"; }; ··· 266 271 01F2AE3F1B82666900D5651A /* SSLCertificateViewController.m */, 267 272 01F879391A4108DD00A63654 /* URLBlocker.h */, 268 273 01F8793A1A4108DD00A63654 /* URLBlocker.m */, 274 + 01BFEE411E3BD6E60069AC83 /* URLBlockerRuleController.h */, 275 + 01BFEE421E3BD6E60069AC83 /* URLBlockerRuleController.m */, 269 276 01801EBE1A335BEC002B4718 /* URLInterceptor.h */, 270 277 01801EBF1A335BEC002B4718 /* URLInterceptor.m */, 271 278 01801E9F1A32CA2A002B4718 /* WebViewController.h */, ··· 291 298 01D7412E1A466AF0007B7033 /* NSString+JavascriptEscape.m */, 292 299 01D741301A49EA14007B7033 /* RuleEditorController.h */, 293 300 01D741311A49EA14007B7033 /* RuleEditorController.m */, 301 + 01BFEE441E3BE1820069AC83 /* RuleEditorRow.h */, 302 + 01BFEE451E3BE1820069AC83 /* RuleEditorRow.m */, 294 303 015748031E208C5000DB2044 /* UIResponder+FirstResponder.h */, 295 304 015748041E208C5000DB2044 /* UIResponder+FirstResponder.m */, 296 305 ); ··· 312 321 01D7412B1A45F8EB007B7033 /* injected.js */, 313 322 01FC0E561B38FB6B00955D9A /* Launch Screen.xib */, 314 323 0135F4751A3D2931005A8F16 /* SearchEngines.plist */, 315 - 01F8794D1A412F8E00A63654 /* urlblocker_targets.plist */, 324 + 01BFEE491E3D3CD60069AC83 /* urlblocker.json */, 316 325 ); 317 326 name = Resources; 318 327 sourceTree = "<group>"; ··· 499 508 018333E91A35746500670CD1 /* https-everywhere_rules.plist in Resources */, 500 509 01801EC31A3360F8002B4718 /* InAppSettings.bundle in Resources */, 501 510 01D7412C1A45F8EB007B7033 /* injected.js in Resources */, 502 - 01F8794F1A412FA500A63654 /* urlblocker_targets.plist in Resources */, 503 511 01801EA61A32CA2A002B4718 /* Images.xcassets in Resources */, 504 512 0141D9701E0C9127003472BC /* fabric.apikey in Resources */, 513 + 01BFEE4A1E3D3CD60069AC83 /* urlblocker.json in Resources */, 505 514 01F8794C1A4124FE00A63654 /* credits.html in Resources */, 506 515 016B2FCB1A53466D002D2730 /* hsts_preload.plist in Resources */, 507 516 01FC0E571B38FB6B00955D9A /* Launch Screen.xib in Resources */, ··· 512 521 isa = PBXResourcesBuildPhase; 513 522 buildActionMask = 2147483647; 514 523 files = ( 515 - 01F8794E1A412F8E00A63654 /* urlblocker_targets.plist in Resources */, 516 524 01F2AE461B827D3E00D5651A /* wildcard.pushover.net.crt in Resources */, 517 525 01F8794B1A41232E00A63654 /* credits.html in Resources */, 518 526 01F879451A41140D00A63654 /* https-everywhere_mock_targets.plist in Resources */, ··· 644 652 014E68A51B9BED3300D98A0A /* HostSettingsController.m in Sources */, 645 653 01F2AE391B7FEF5E00D5651A /* SSLCertificate.m in Sources */, 646 654 01801E9B1A32CA2A002B4718 /* AppDelegate.m in Sources */, 655 + 01BFEE461E3BE1820069AC83 /* RuleEditorRow.m in Sources */, 647 656 01AFEB491B4ED48000A02482 /* Bookmark.m in Sources */, 648 657 01801E981A32CA2A002B4718 /* main.m in Sources */, 649 658 01D741321A49EA14007B7033 /* RuleEditorController.m in Sources */, ··· 652 661 01801EA11A32CA2A002B4718 /* WebViewController.m in Sources */, 653 662 019516CF1B7D8A0C0078586A /* NSData+CocoaDevUsersAdditions.m in Sources */, 654 663 01F8793B1A4108DD00A63654 /* URLBlocker.m in Sources */, 664 + 01BFEE431E3BD6E60069AC83 /* URLBlockerRuleController.m in Sources */, 655 665 01801EC01A335BEC002B4718 /* URLInterceptor.m in Sources */, 656 666 01F2AE401B82666900D5651A /* SSLCertificateViewController.m in Sources */, 657 667 01DD9E051E0F151B00C8DF07 /* README.md in Sources */,
-1
Endless/HTTPSEverywhereRuleController.m
··· 43 43 return self; 44 44 } 45 45 46 - 47 46 - (NSString *)ruleDisabledReason:(RuleEditorRow *)row 48 47 { 49 48 return [[HTTPSEverywhere disabledRules] objectForKey:[row key]];
-245
Endless/Resources/urlblocker_targets.plist
··· 1 - <!-- generated from urlblocker.json - do not directly edit this file --> 2 - <?xml version="1.0" encoding="UTF-8"?> 3 - <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 4 - <plist version="1.0"> 5 - <dict> 6 - <key>1e100.net</key> 7 - <string>Advertising/Analytics - Google</string> 8 - <key>2mdn.net</key> 9 - <string>Advertising/Analytics - Google</string> 10 - <key>3gl.net</key> 11 - <string>Analytics - Catchpoint</string> 12 - <key>3lift.com</key> 13 - <string>Advertising/Analytics - TripleLift</string> 14 - <key>adchoice.feedsportal.com</key> 15 - <string>Advertising/Analytics - Mediafed</string> 16 - <key>addthis.com</key> 17 - <string>Social - AddThis</string> 18 - <key>adform.com</key> 19 - <string>Advertising/Analytics - Adform</string> 20 - <key>adform.net</key> 21 - <string>Advertising/Analytics - Adform</string> 22 - <key>adnexusmedia.com</key> 23 - <string>Advertising/Analytics - AppNexus</string> 24 - <key>adnxs.com</key> 25 - <string>Advertising/Analytics - AppNexus</string> 26 - <key>adobetag.com</key> 27 - <string>Analytics - Adobe</string> 28 - <key>adroll.com</key> 29 - <string>Advertising/Analytics - AdRoll</string> 30 - <key>ads.yahoo.com</key> 31 - <string>Advertising/Analytics - Yahoo</string> 32 - <key>adsafeprotected.com</key> 33 - <string>Advertising/Analytics - Integral Ad Science</string> 34 - <key>adsrvr.org</key> 35 - <string>Advertising/Analytics - The Trade Desk</string> 36 - <key>advertisation.com</key> 37 - <string>Advertising/Analytics - Advertisation</string> 38 - <key>adzerk.net</key> 39 - <string>Advertising/Analytics - Adzerk</string> 40 - <key>amazon-adsystem.com</key> 41 - <string>Advertising/Analytics - Amazon</string> 42 - <key>apis.google.com</key> 43 - <string>Advertising/Analytics - Google</string> 44 - <key>appnexus.com</key> 45 - <string>Advertising/Analytics - AppNexus</string> 46 - <key>autoforums.com</key> 47 - <string>Advertising/Analytics - AutoGuide</string> 48 - <key>bdex.com</key> 49 - <string>Advertising/Analytics - BDEX</string> 50 - <key>betrad.com</key> 51 - <string>Advertising/Analytics - Evidon</string> 52 - <key>bkrtx.com</key> 53 - <string>Unknown - bktrx.com</string> 54 - <key>bluecava.com</key> 55 - <string>Analytics - BlueCava</string> 56 - <key>bluelithium.com</key> 57 - <string>Advertising/Analytics - Yahoo</string> 58 - <key>btstatic.com</key> 59 - <string>Advertising/Analytics - Signal/BrightTag</string> 60 - <key>catchpoint.com</key> 61 - <string>Analytics - Catchpoint</string> 62 - <key>chartbeat.com</key> 63 - <string>Analytics - Chartbeat</string> 64 - <key>chartbeat.net</key> 65 - <string>Analytics - Chartbeat</string> 66 - <key>clicktale.net</key> 67 - <string>Analytics - Clicktale</string> 68 - <key>clicky.com</key> 69 - <string>Analytics - Clicky</string> 70 - <key>cmcore.com</key> 71 - <string>Analytics - IBM Digital Analytics</string> 72 - <key>criteo.com</key> 73 - <string>Advertising/Analytics - Criteo</string> 74 - <key>criteo.net</key> 75 - <string>Advertising/Analytics - Criteo</string> 76 - <key>crsspxl.com</key> 77 - <string>Analytics - Cross Pixel</string> 78 - <key>crwdcntrl.net</key> 79 - <string>Advertising/Analytics - Lotame</string> 80 - <key>cxense.com</key> 81 - <string>Advertising/Analytics - Cxense</string> 82 - <key>datalogix.com</key> 83 - <string>Analytics - Datalogix</string> 84 - <key>demdex.net</key> 85 - <string>Analytics - Adobe</string> 86 - <key>domdex.com</key> 87 - <string>Advertising/Analytics - Magnetic</string> 88 - <key>doubleclick.net</key> 89 - <string>Advertising/Analytics - Google</string> 90 - <key>doubleverify.com</key> 91 - <string>Analytics - DoubleVerify</string> 92 - <key>errorception.com</key> 93 - <string>Analytics - Errorception</string> 94 - <key>eventlogger.soundcloud.com</key> 95 - <string>Social - Soundcloud</string> 96 - <key>evidon.com</key> 97 - <string>Advertising/Analytics - Evidon</string> 98 - <key>exelator.com</key> 99 - <string>Advertising/Analytics - eXelate</string> 100 - <key>facebook.com</key> 101 - <string>Social - Facebook</string> 102 - <key>facebook.net</key> 103 - <string>Social - Facebook</string> 104 - <key>fb.com</key> 105 - <string>Social - Facebook</string> 106 - <key>flattr.com</key> 107 - <string>Social - Flattr</string> 108 - <key>fyre.co</key> 109 - <string>Advertising/Analytics - Livefyre</string> 110 - <key>getclicky.com</key> 111 - <string>Analytics - Clicky</string> 112 - <key>go-mpulse.net</key> 113 - <string>Analytics - mPulse</string> 114 - <key>google-analytics.com</key> 115 - <string>Advertising/Analytics - Google</string> 116 - <key>googleadservices.com</key> 117 - <string>Advertising/Analytics - Google</string> 118 - <key>googlesyndication.com</key> 119 - <string>Advertising/Analytics - Google</string> 120 - <key>googletagservices.com</key> 121 - <string>Advertising/Analytics - Google</string> 122 - <key>imrworldwide.com</key> 123 - <string>Analytics - Nielsen</string> 124 - <key>integralads.com</key> 125 - <string>Advertising/Analytics - Integral Ad Science</string> 126 - <key>intentiq.com</key> 127 - <string>Analytics - Intent IQ</string> 128 - <key>intermarkets.net</key> 129 - <string>Advertising/Analytics - Intermarkets</string> 130 - <key>krux.com</key> 131 - <string>Analytics - Krux</string> 132 - <key>krxd.net</key> 133 - <string>Analytics - Krux</string> 134 - <key>liadm.com</key> 135 - <string>Advertising/Analytics - Live Intent</string> 136 - <key>linkedin.com</key> 137 - <string>Social - LinkedIn</string> 138 - <key>livefyre.com</key> 139 - <string>Advertising/Analytics - Livefyre</string> 140 - <key>liveintent.com</key> 141 - <string>Advertising/Analytics - Live Intent</string> 142 - <key>liveramp.com</key> 143 - <string>Advertising/Analytics - LiveRamp</string> 144 - <key>lotame.com</key> 145 - <string>Advertising/Analytics - Lotame</string> 146 - <key>magnetic.com</key> 147 - <string>Advertising/Analytics - Magnetic</string> 148 - <key>moat.com</key> 149 - <string>Advertising/Analytics - Moat</string> 150 - <key>moatads.com</key> 151 - <string>Advertising/Analytics - Moat</string> 152 - <key>mookie1.com</key> 153 - <string>Advertising/Analytics - Media Innovation Group</string> 154 - <key>nativo.net</key> 155 - <string>Advertising/Analytics - Nativo</string> 156 - <key>nbcudigitaladops.com</key> 157 - <string>Advertising/Analytics - NBC</string> 158 - <key>newrelic.com</key> 159 - <string>Analytics - New Relic</string> 160 - <key>nexac.com</key> 161 - <string>Analytics - Datalogix</string> 162 - <key>nielsen.com</key> 163 - <string>Analytics - Nielsen</string> 164 - <key>nvg.msnbc.com</key> 165 - <string>Advertising/Analytics - NBC</string> 166 - <key>optimizely.com</key> 167 - <string>Analytics - Optimizely</string> 168 - <key>outbrain.com</key> 169 - <string>Advertising/Analytics - Outbrain</string> 170 - <key>pagefair.com</key> 171 - <string>Analytics - PageFair</string> 172 - <key>pagefair.net</key> 173 - <string>Analytics - PageFair</string> 174 - <key>parsely.com</key> 175 - <string>Analytics - Parsely</string> 176 - <key>peer39.net</key> 177 - <string>Advertising/Analytics - Peer39</string> 178 - <key>pixel.redditmedia.com</key> 179 - <string>Analytics - Reddit</string> 180 - <key>pixel.wp.com</key> 181 - <string>Analytics - WordPress</string> 182 - <key>postrelease.com</key> 183 - <string>Advertising/Analytics - Nativo</string> 184 - <key>pro-market.net</key> 185 - <string>Unknown - pro-market</string> 186 - <key>quantserve.com</key> 187 - <string>Analytics - Quantcast</string> 188 - <key>questionmarket.com</key> 189 - <string>Advertising/Analytics - Safecount</string> 190 - <key>reddit.com</key> 191 - <string>Social - Reddit</string> 192 - <key>rlcdn.com</key> 193 - <string>Advertising/Analytics - LiveRamp</string> 194 - <key>rubiconproject.com</key> 195 - <string>Advertising/Analytics - Rubicon Project</string> 196 - <key>safecount.net</key> 197 - <string>Advertising/Analytics - Safecount</string> 198 - <key>scorecardresearch.com</key> 199 - <string>Analytics - comScore</string> 200 - <key>serving-sys.com</key> 201 - <string>Advertising/Analytics - MediaMind</string> 202 - <key>sharethis.com</key> 203 - <string>Social - ShareThis</string> 204 - <key>signal.co</key> 205 - <string>Advertising/Analytics - Signal/BrightTag</string> 206 - <key>simpli.fi</key> 207 - <string>Advertising/Analytics - Simplifi</string> 208 - <key>sizmek.com</key> 209 - <string>Advertising/Analytics - MediaMind</string> 210 - <key>statcounter.com</key> 211 - <string>Analytics - StatCounter</string> 212 - <key>stats.redditmedia.com</key> 213 - <string>Analytics - Reddit</string> 214 - <key>stats.wp.com</key> 215 - <string>Analytics - WordPress</string> 216 - <key>taboola.com</key> 217 - <string>Advertising/Analytics - Taboola</string> 218 - <key>thebrighttag.com</key> 219 - <string>Advertising/Analytics - Signal/BrightTag</string> 220 - <key>themig.com</key> 221 - <string>Advertising/Analytics - Media Innovation Group</string> 222 - <key>thetradedesk.com</key> 223 - <string>Advertising/Analytics - The Trade Desk</string> 224 - <key>thoughtleadr.com</key> 225 - <string>Advertising/Analytics - ThoughtLeadr</string> 226 - <key>triplelift.com</key> 227 - <string>Advertising/Analytics - TripleLift</string> 228 - <key>turn.com</key> 229 - <string>Analytics - Turn</string> 230 - <key>twitter.com</key> 231 - <string>Social - Twitter</string> 232 - <key>unruly.co</key> 233 - <string>Analytics - Unruly</string> 234 - <key>unrulymedia.com</key> 235 - <string>Analytics - Unruly</string> 236 - <key>usabilla.com</key> 237 - <string>Analytics - Usabilla</string> 238 - <key>verticalscope.com</key> 239 - <string>Advertising/Analytics - VerticalScope</string> 240 - <key>viglink.com</key> 241 - <string>Advertising/Analytics - VigLink</string> 242 - <key>yieldmo.com</key> 243 - <string>Advertising/Analytics - YieldMo</string> 244 - </dict> 245 - </plist>
+1
Endless/RuleEditorController.m
··· 80 80 if (disabled == nil) { 81 81 cell.textLabel.textColor = [UIColor darkTextColor]; 82 82 cell.detailTextLabel.text = [row detailTextLabel]; 83 + cell.detailTextLabel.textColor = [UIColor darkGrayColor]; 83 84 } 84 85 else { 85 86 cell.textLabel.textColor = [UIColor redColor];
+6 -2
Endless/URLBlocker.h
··· 1 1 /* 2 2 * Endless 3 - * Copyright (c) 2014-2015 joshua stein <jcs@jcs.org> 3 + * Copyright (c) 2014-2017 joshua stein <jcs@jcs.org> 4 4 * 5 5 * See LICENSE file for redistribution terms. 6 6 */ ··· 10 10 @interface URLBlocker : NSObject 11 11 12 12 + (NSDictionary *)targets; 13 + + (NSMutableDictionary *)disabledTargets; 14 + + (void)saveDisabledTargets; 13 15 14 16 + (BOOL)shouldBlockURL:(NSURL *)url; 15 - + (BOOL)shouldBlockURL:(NSURL *)url fromMainDocumentURL:(NSURL *)mainUrl; 17 + + (NSString *)blockingTargetForURL:(NSURL *)url fromMainDocumentURL:(NSURL *)mainUrl; 18 + + (void)enableTargetByHost:(NSString *)target; 19 + + (void)disableTargetByHost:(NSString *)target withReason:(NSString *)reason; 16 20 17 21 @end
+84 -12
Endless/URLBlocker.m
··· 10 10 @implementation URLBlocker 11 11 12 12 static NSDictionary *_targets; 13 + static NSMutableDictionary *_disabledTargets; 13 14 static NSCache *ruleCache; 14 15 15 16 #define RULE_CACHE_SIZE 20 16 17 18 + + (NSString *)disabledTargetsPath 19 + { 20 + NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; 21 + return [path stringByAppendingPathComponent:@"url_blocker_disabled.plist"]; 22 + } 23 + 17 24 + (NSDictionary *)targets 18 25 { 26 + NSError *error; 27 + 19 28 if (_targets == nil) { 20 - NSString *path = [[NSBundle mainBundle] pathForResource:@"urlblocker_targets" ofType:@"plist"]; 29 + NSString *path = [[NSBundle mainBundle] pathForResource:@"urlblocker" ofType:@"json"]; 21 30 if (![[NSFileManager defaultManager] fileExistsAtPath:path]) { 22 31 NSLog(@"[URLBlocker] no target plist at %@", path); 23 32 abort(); 24 33 } 25 34 26 - _targets = [NSDictionary dictionaryWithContentsOfFile:path]; 35 + NSInputStream *input = [[NSInputStream alloc] initWithFileAtPath:path]; 36 + if (input != nil) { 37 + [input open]; 38 + 39 + NSDictionary *blockers = [NSJSONSerialization JSONObjectWithStream:input options:kNilOptions error:&error]; 40 + if (error != nil) 41 + NSLog(@"[URLBlocker] couldn't read %@: %@", path, error); 42 + [input close]; 43 + 44 + /* convert from { "desc" => [ "host1", "host2" ] } to { "host1" => "desc", "host2" => "desc" } */ 45 + NSMutableDictionary *ttargets = [[NSMutableDictionary alloc] init]; 46 + for (NSString *key in [blockers allKeys]) { 47 + NSArray *doms = [blockers objectForKey:key]; 48 + for (NSString *dom in doms) 49 + [ttargets setObject:key forKey:dom]; 50 + } 51 + 52 + _targets = [NSDictionary dictionaryWithDictionary:ttargets]; 53 + } 54 + 55 + if (!_targets || ![_targets count]) { 56 + NSLog(@"[URLBlocker] couldn't read %@", path); 57 + _targets = @{}; 58 + return _targets; 59 + } 27 60 28 61 #ifdef TRACE_URL_BLOCKER 29 62 NSLog(@"[URLBlocker] locked and loaded with %lu target domains", [_targets count]); ··· 33 66 return _targets; 34 67 } 35 68 69 + + (NSMutableDictionary *)disabledTargets 70 + { 71 + if (_disabledTargets == nil) { 72 + NSString *path = [[self class] disabledTargetsPath]; 73 + if ([[NSFileManager defaultManager] fileExistsAtPath:path]) { 74 + _disabledTargets = [NSMutableDictionary dictionaryWithContentsOfFile:path]; 75 + 76 + #ifdef TRACE_HTTPS_EVERYWHERE 77 + NSLog(@"[URLBlocker] loaded %lu disabled targets", [_disabledTargets count]); 78 + #endif 79 + } 80 + else { 81 + _disabledTargets = [[NSMutableDictionary alloc] init]; 82 + } 83 + } 84 + 85 + return _disabledTargets; 86 + } 87 + 88 + + (void)saveDisabledTargets 89 + { 90 + [_disabledTargets writeToFile:[[self class] disabledTargetsPath] atomically:YES]; 91 + } 92 + 36 93 + (void)cacheBlockedURL:(NSURL *)url withRule:(NSString *)rule 37 94 { 38 95 if (!ruleCache) { ··· 50 107 if (!(ruleCache && (blocker = [ruleCache objectForKey:url]))) { 51 108 NSString *host = [[url host] lowercaseString]; 52 109 53 - blocker = [[[self class] targets] objectForKey:host]; 54 - 55 - if (!blocker) { 110 + if ([[[self class] targets] objectForKey:host]) 111 + blocker = host; 112 + else { 56 113 /* now for x.y.z.example.com, try *.y.z.example.com, *.z.example.com, *.example.com, etc. */ 57 114 /* TODO: should we skip the last component for obviously non-matching things like "*.com", "*.net"? */ 58 115 NSArray *hostp = [host componentsSeparatedByString:@"."]; 59 116 for (int i = 1; i < [hostp count]; i++) { 60 117 NSString *wc = [[hostp subarrayWithRange:NSMakeRange(i, [hostp count] - i)] componentsJoinedByString:@"."]; 61 118 62 - if ((blocker = [[[self class] targets] objectForKey:wc]) != nil) { 119 + if ([[[self class] targets] objectForKey:wc]) { 120 + blocker = wc; 63 121 break; 64 122 } 65 123 } 66 124 } 67 125 } 68 126 69 - if (blocker) { 127 + if (blocker && [[URLBlocker disabledTargets] objectForKey:blocker] != nil) 128 + return nil; 129 + 130 + if (blocker) 70 131 [[self class] cacheBlockedURL:url withRule:blocker]; 71 - } 72 132 73 133 return blocker; 74 134 } ··· 78 138 return ([self blockRuleForURL:url] != nil); 79 139 } 80 140 81 - + (BOOL)shouldBlockURL:(NSURL *)url fromMainDocumentURL:(NSURL *)mainUrl 141 + + (NSString *)blockingTargetForURL:(NSURL *)url fromMainDocumentURL:(NSURL *)mainUrl 82 142 { 83 143 NSString *blocker = [self blockRuleForURL:url]; 84 144 if (blocker != nil && mainUrl != nil) { 85 145 /* if this same rule would have blocked our main URL, allow it since the user is probably viewing this site and this isn't a sneaky tracker */ 86 146 if ([blocker isEqualToString:[self blockRuleForURL:mainUrl]]) { 87 - return NO; 147 + return nil; 88 148 } 89 149 90 150 #ifdef TRACE_URL_BLOCKER 91 151 NSLog(@"[URLBlocker] blocking %@ (via %@) (%@)", url, mainUrl, blocker); 92 152 #endif 93 153 94 - return YES; 154 + return blocker; 95 155 } 96 156 97 - return NO; 157 + return nil; 158 + } 159 + 160 + + (void)enableTargetByHost:(NSString *)target 161 + { 162 + [[[self class] disabledTargets] removeObjectForKey:target]; 163 + [[self class] saveDisabledTargets]; 164 + } 165 + 166 + + (void)disableTargetByHost:(NSString *)target withReason:(NSString *)reason 167 + { 168 + [[[self class] disabledTargets] setObject:reason forKey:target]; 169 + [[self class] saveDisabledTargets]; 98 170 } 99 171 100 172 @end
+12
Endless/URLBlockerRuleController.h
··· 1 + /* 2 + * Endless 3 + * Copyright (c) 2017 joshua stein <jcs@jcs.org> 4 + * 5 + * See LICENSE file for redistribution terms. 6 + */ 7 + 8 + #import "RuleEditorController.h" 9 + 10 + @interface URLBlockerRuleController : RuleEditorController 11 + 12 + @end
+63
Endless/URLBlockerRuleController.m
··· 1 + /* 2 + * Endless 3 + * Copyright (c) 2017 joshua stein <jcs@jcs.org> 4 + * 5 + * See LICENSE file for redistribution terms. 6 + */ 7 + 8 + #import "URLBlockerRuleController.h" 9 + #import "URLBlocker.h" 10 + 11 + #import "HTTPSEverywhere.h" 12 + 13 + @implementation URLBlockerRuleController 14 + 15 + - (id)initWithStyle:(UITableViewStyle)style 16 + { 17 + self = [super initWithStyle:style]; 18 + 19 + self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self.navigationController action:@selector(dismissModalViewControllerAnimated:)]; 20 + 21 + self.sortedRuleRows = [[NSMutableArray alloc] initWithCapacity:[[URLBlocker targets] count]]; 22 + self.inUseRuleRows = [[NSMutableArray alloc] init]; 23 + 24 + NSDictionary *inUse = nil; 25 + if ([[self.appDelegate webViewController] curWebViewTab] != nil) 26 + inUse = [[[self.appDelegate webViewController] curWebViewTab] applicableURLBlockerTargets]; 27 + 28 + for (NSString *k in [[[URLBlocker targets] allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]) { 29 + RuleEditorRow *row = [[RuleEditorRow alloc] init]; 30 + row.key = k; 31 + row.textLabel = k; 32 + row.detailTextLabel = [[URLBlocker targets] objectForKey:k]; 33 + 34 + if (inUse && [inUse objectForKey:k]) 35 + [self.inUseRuleRows addObject:row]; 36 + else 37 + [self.sortedRuleRows addObject:row]; 38 + } 39 + 40 + self.inUseRuleRows = [NSMutableArray arrayWithArray:self.inUseRuleRows]; 41 + self.sortedRuleRows = [NSMutableArray arrayWithArray:self.sortedRuleRows]; 42 + 43 + self.title = @"Blocked 3rd-Party Hosts"; 44 + 45 + return self; 46 + } 47 + 48 + - (NSString *)ruleDisabledReason:(RuleEditorRow *)row 49 + { 50 + return [[URLBlocker disabledTargets] objectForKey:[row key]]; 51 + } 52 + 53 + - (void)disableRuleForRow:(RuleEditorRow *)row withReason:(NSString *)reason 54 + { 55 + [URLBlocker disableTargetByHost:[row key] withReason:reason]; 56 + } 57 + 58 + - (void)enableRuleForRow:(RuleEditorRow *)row 59 + { 60 + [URLBlocker enableTargetByHost:[row key]]; 61 + } 62 + 63 + @end
+7 -3
Endless/URLInterceptor.m
··· 269 269 if (self.isOrigin) { 270 270 [LocalNetworkChecker clearCache]; 271 271 } 272 - else if ([URLBlocker shouldBlockURL:[newRequest URL] fromMainDocumentURL:[newRequest mainDocumentURL]]) { 273 - cancelLoading(); 274 - return; 272 + else { 273 + NSString *blocker = [URLBlocker blockingTargetForURL:[newRequest URL] fromMainDocumentURL:[newRequest mainDocumentURL]]; 274 + if (blocker != nil) { 275 + [[wvt applicableURLBlockerTargets] setObject:@YES forKey:blocker]; 276 + cancelLoading(); 277 + return; 278 + } 275 279 } 276 280 277 281 /* some rules act on the host we're connecting to, and some act on the origin host */
+17
Endless/WebViewMenuController.m
··· 12 12 #import "HostSettingsController.h" 13 13 #import "IASKAppSettingsViewController.h" 14 14 #import "HTTPSEverywhereRuleController.h" 15 + #import "URLBlockerRuleController.h" 15 16 #import "WebViewMenuController.h" 16 17 17 18 #import "OnePasswordExtension.h" ··· 46 47 47 48 [buttons addObject:@{ FUNC : @"menuAddOrManageBookmarks", LABEL : @"Manage Bookmarks" }]; 48 49 [buttons addObject:@{ FUNC : @"menuShare", LABEL : @"Share URL" }]; 50 + [buttons addObject:@{ FUNC : @"menuURLBlocker", LABEL : @"URL Blocker" }]; 49 51 [buttons addObject:@{ FUNC : @"menuHTTPSEverywhere", LABEL : @"HTTPS Everywhere" }]; 50 52 [buttons addObject:@{ FUNC : @"menuHostSettings", LABEL : @"Host Settings" }]; 51 53 [buttons addObject:@{ FUNC : @"menuSettings", LABEL : @"Global Settings" }]; ··· 113 115 cell.userInteractionEnabled = haveURL; 114 116 cell.textLabel.enabled = haveURL; 115 117 } 118 + else if ([func isEqualToString:@"menuURLBlocker"] && haveURL) { 119 + long ruleCount = [[[[appDelegate webViewController] curWebViewTab] applicableURLBlockerTargets] count]; 120 + 121 + if (ruleCount > 0) { 122 + cell.detailTextLabel.text = [NSString stringWithFormat:@"%ld host%@ blocked", ruleCount, (ruleCount == 1 ? @"" : @"s")]; 123 + cell.detailTextLabel.textColor = [self colorForMenuTextHighlight]; 124 + } 125 + } 116 126 else if ([func isEqualToString:@"menuHTTPSEverywhere"] && haveURL) { 117 127 long ruleCount = [[[[appDelegate webViewController] curWebViewTab] applicableHTTPSEverywhereRules] count]; 118 128 ··· 179 189 if (!success) 180 190 NSLog(@"[OnePasswordExtension] failed to fill into webview: %@", error); 181 191 }]; 192 + } 193 + 194 + - (void)menuURLBlocker 195 + { 196 + URLBlockerRuleController *ubrc = [[URLBlockerRuleController alloc] init]; 197 + UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:ubrc]; 198 + [[appDelegate webViewController] presentViewController:navController animated:YES completion:nil]; 182 199 } 183 200 184 201 - (void)menuHTTPSEverywhere
+1
Endless/WebViewTab.h
··· 109 109 @property WebViewTabSecureMode secureMode; 110 110 @property (strong, nonatomic) SSLCertificate *SSLCertificate; 111 111 @property NSMutableDictionary *applicableHTTPSEverywhereRules; 112 + @property NSMutableDictionary *applicableURLBlockerTargets; 112 113 113 114 /* for javascript IPC */ 114 115 @property (strong, atomic) NSString *randID;
+4 -2
Endless/WebViewTab.m
··· 111 111 [self zoomNormal]; 112 112 113 113 [self setSecureMode:WebViewTabSecureModeInsecure]; 114 - [self setApplicableHTTPSEverywhereRules:[[NSMutableDictionary alloc] initWithCapacity:6]]; 115 - 114 + [self setApplicableHTTPSEverywhereRules:[[NSMutableDictionary alloc] init]]; 115 + [self setApplicableURLBlockerTargets:[[NSMutableDictionary alloc] init]]; 116 + 116 117 UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressMenu:)]; 117 118 [lpgr setDelegate:self]; 118 119 [_webView addGestureRecognizer:lpgr]; ··· 210 211 - (void)prepareForNewURL:(NSURL *)URL 211 212 { 212 213 [[self applicableHTTPSEverywhereRules] removeAllObjects]; 214 + [[self applicableURLBlockerTargets] removeAllObjects]; 213 215 [self setSSLCertificate:nil]; 214 216 [self setUrl:URL]; 215 217 }
-21
convert_rules.rb
··· 15 15 HTTPS_E_TARGETS_PLIST = "Endless/Resources/https-everywhere_targets.plist" 16 16 HTTPS_E_RULES_PLIST = "Endless/Resources/https-everywhere_rules.plist" 17 17 18 - URLBLOCKER_JSON = "urlblocker.json" 19 - URLBLOCKER_TARGETS_PLIST = "Endless/Resources/urlblocker_targets.plist" 20 - 21 18 # in b64 for some reason 22 19 HSTS_PRELOAD_LIST = "https://chromium.googlesource.com/chromium/src/net/+/master/http/transport_security_state_static.json?format=TEXT" 23 20 HSTS_PRELOAD_HOSTS_PLIST = "Endless/Resources/hsts_preload.plist" ··· 107 104 rules.to_plist) 108 105 end 109 106 110 - # convert JSON ruleset into a list of target domains and a list of rulesets 111 - # with information URLs 112 - def convert_urlblocker 113 - targets = {} 114 - 115 - JSON.parse(File.read(URLBLOCKER_JSON)).each do |company,domains| 116 - domains.each do |dom| 117 - targets[dom] = company 118 - end 119 - end 120 - 121 - File.write(URLBLOCKER_TARGETS_PLIST, 122 - "<!-- generated from #{URLBLOCKER_JSON} - do not directly edit this " + 123 - "file -->\n" + 124 - targets.to_plist) 125 - end 126 - 127 107 def convert_hsts_preload 128 108 domains = {} 129 109 ··· 141 121 end 142 122 143 123 convert_https_e 144 - convert_urlblocker 145 124 convert_hsts_preload
+4
urlblocker.json Endless/Resources/urlblocker.json
··· 350 350 ,"Unknown - bktrx.com": [ 351 351 "bkrtx.com" 352 352 ] 353 + 354 + ,"Testing - Endless": [ 355 + "blocked.endl.es" 356 + ] 353 357 }