@@ -48,34 +48,34 @@ namespace Shadowsocks.Controller | |||||
public static bool MergeAndWritePACFile(string gfwListResult) | public static bool MergeAndWritePACFile(string gfwListResult) | ||||
{ | { | ||||
string abpContent = MergePACFile(gfwListResult); | string abpContent = MergePACFile(gfwListResult); | ||||
if (File.Exists(PACServer.PAC_FILE)) | |||||
if (File.Exists(PACDaemon.PAC_FILE)) | |||||
{ | { | ||||
string original = FileManager.NonExclusiveReadAllText(PACServer.PAC_FILE, Encoding.UTF8); | |||||
string original = FileManager.NonExclusiveReadAllText(PACDaemon.PAC_FILE, Encoding.UTF8); | |||||
if (original == abpContent) | if (original == abpContent) | ||||
{ | { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8); | |||||
File.WriteAllText(PACDaemon.PAC_FILE, abpContent, Encoding.UTF8); | |||||
return true; | return true; | ||||
} | } | ||||
private static string MergePACFile(string gfwListResult) | private static string MergePACFile(string gfwListResult) | ||||
{ | { | ||||
string abpContent; | string abpContent; | ||||
if (File.Exists(PACServer.USER_ABP_FILE)) | |||||
if (File.Exists(PACDaemon.USER_ABP_FILE)) | |||||
{ | { | ||||
abpContent = FileManager.NonExclusiveReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8); | |||||
abpContent = FileManager.NonExclusiveReadAllText(PACDaemon.USER_ABP_FILE, Encoding.UTF8); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
abpContent = Utils.UnGzip(Resources.abp_js); | |||||
abpContent = Resources.abp_js; | |||||
} | } | ||||
List<string> userruleLines = new List<string>(); | List<string> userruleLines = new List<string>(); | ||||
if (File.Exists(PACServer.USER_RULE_FILE)) | |||||
if (File.Exists(PACDaemon.USER_RULE_FILE)) | |||||
{ | { | ||||
string userrulesString = FileManager.NonExclusiveReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8); | |||||
string userrulesString = FileManager.NonExclusiveReadAllText(PACDaemon.USER_RULE_FILE, Encoding.UTF8); | |||||
userruleLines = ParseToValidList(userrulesString); | userruleLines = ParseToValidList(userrulesString); | ||||
} | } | ||||
@@ -0,0 +1,130 @@ | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Util; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Shadowsocks.Controller | |||||
{ | |||||
/// <summary> | |||||
/// Processing the PAC file content | |||||
/// </summary> | |||||
public class PACDaemon | |||||
{ | |||||
public const string PAC_FILE = "pac.txt"; | |||||
public const string USER_RULE_FILE = "user-rule.txt"; | |||||
public const string USER_ABP_FILE = "abp.txt"; | |||||
FileSystemWatcher PACFileWatcher; | |||||
FileSystemWatcher UserRuleFileWatcher; | |||||
public event EventHandler PACFileChanged; | |||||
public event EventHandler UserRuleFileChanged; | |||||
public PACDaemon() | |||||
{ | |||||
TouchPACFile(); | |||||
TouchUserRuleFile(); | |||||
this.WatchPacFile(); | |||||
this.WatchUserRuleFile(); | |||||
} | |||||
public string TouchPACFile() | |||||
{ | |||||
if (!File.Exists(PAC_FILE)) | |||||
{ | |||||
File.WriteAllText(PAC_FILE, Resources.proxy_pac_txt); | |||||
} | |||||
return PAC_FILE; | |||||
} | |||||
internal string TouchUserRuleFile() | |||||
{ | |||||
if (!File.Exists(USER_RULE_FILE)) | |||||
{ | |||||
File.WriteAllText(USER_RULE_FILE, Resources.user_rule); | |||||
} | |||||
return USER_RULE_FILE; | |||||
} | |||||
internal string GetPACContent() | |||||
{ | |||||
if (File.Exists(PAC_FILE)) | |||||
{ | |||||
return File.ReadAllText(PAC_FILE, Encoding.UTF8); | |||||
} | |||||
else | |||||
{ | |||||
return Resources.proxy_pac_txt; | |||||
} | |||||
} | |||||
private void WatchPacFile() | |||||
{ | |||||
PACFileWatcher?.Dispose(); | |||||
PACFileWatcher = new FileSystemWatcher(Directory.GetCurrentDirectory()); | |||||
PACFileWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; | |||||
PACFileWatcher.Filter = PAC_FILE; | |||||
PACFileWatcher.Changed += PACFileWatcher_Changed; | |||||
PACFileWatcher.Created += PACFileWatcher_Changed; | |||||
PACFileWatcher.Deleted += PACFileWatcher_Changed; | |||||
PACFileWatcher.Renamed += PACFileWatcher_Changed; | |||||
PACFileWatcher.EnableRaisingEvents = true; | |||||
} | |||||
private void WatchUserRuleFile() | |||||
{ | |||||
UserRuleFileWatcher?.Dispose(); | |||||
UserRuleFileWatcher = new FileSystemWatcher(Directory.GetCurrentDirectory()); | |||||
UserRuleFileWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; | |||||
UserRuleFileWatcher.Filter = USER_RULE_FILE; | |||||
UserRuleFileWatcher.Changed += UserRuleFileWatcher_Changed; | |||||
UserRuleFileWatcher.Created += UserRuleFileWatcher_Changed; | |||||
UserRuleFileWatcher.Deleted += UserRuleFileWatcher_Changed; | |||||
UserRuleFileWatcher.Renamed += UserRuleFileWatcher_Changed; | |||||
UserRuleFileWatcher.EnableRaisingEvents = true; | |||||
} | |||||
#region FileSystemWatcher.OnChanged() | |||||
// FileSystemWatcher Changed event is raised twice | |||||
// http://stackoverflow.com/questions/1764809/filesystemwatcher-changed-event-is-raised-twice | |||||
// Add a short delay to avoid raise event twice in a short period | |||||
private void PACFileWatcher_Changed(object sender, FileSystemEventArgs e) | |||||
{ | |||||
if (PACFileChanged != null) | |||||
{ | |||||
Logging.Info($"Detected: PAC file '{e.Name}' was {e.ChangeType.ToString().ToLower()}."); | |||||
Task.Factory.StartNew(() => | |||||
{ | |||||
((FileSystemWatcher)sender).EnableRaisingEvents = false; | |||||
System.Threading.Thread.Sleep(10); | |||||
PACFileChanged(this, new EventArgs()); | |||||
((FileSystemWatcher)sender).EnableRaisingEvents = true; | |||||
}); | |||||
} | |||||
} | |||||
private void UserRuleFileWatcher_Changed(object sender, FileSystemEventArgs e) | |||||
{ | |||||
if (UserRuleFileChanged != null) | |||||
{ | |||||
Logging.Info($"Detected: User Rule file '{e.Name}' was {e.ChangeType.ToString().ToLower()}."); | |||||
Task.Factory.StartNew(() => | |||||
{ | |||||
((FileSystemWatcher)sender).EnableRaisingEvents = false; | |||||
System.Threading.Thread.Sleep(10); | |||||
UserRuleFileChanged(this, new EventArgs()); | |||||
((FileSystemWatcher)sender).EnableRaisingEvents = true; | |||||
}); | |||||
} | |||||
} | |||||
#endregion | |||||
} | |||||
} |
@@ -15,28 +15,21 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
public class PACServer : Listener.Service | public class PACServer : Listener.Service | ||||
{ | { | ||||
public const string PAC_FILE = "pac.txt"; | |||||
public const string USER_RULE_FILE = "user-rule.txt"; | |||||
public const string USER_ABP_FILE = "abp.txt"; | |||||
public const string RESOURCE_NAME = "pac"; | |||||
private string PacSecret { get; set; } = ""; | private string PacSecret { get; set; } = ""; | ||||
public string PacUrl { get; private set; } = ""; | public string PacUrl { get; private set; } = ""; | ||||
FileSystemWatcher PACFileWatcher; | |||||
FileSystemWatcher UserRuleFileWatcher; | |||||
private Configuration _config; | private Configuration _config; | ||||
private PACDaemon _pacDaemon; | |||||
public event EventHandler PACFileChanged; | |||||
public event EventHandler UserRuleFileChanged; | |||||
public PACServer() | |||||
public PACServer(PACDaemon pacDaemon) | |||||
{ | { | ||||
this.WatchPacFile(); | |||||
this.WatchUserRuleFile(); | |||||
_pacDaemon = pacDaemon; | |||||
} | } | ||||
public void UpdateConfiguration(Configuration config) | |||||
public void UpdatePACURL(Configuration config) | |||||
{ | { | ||||
this._config = config; | this._config = config; | ||||
@@ -51,7 +44,7 @@ namespace Shadowsocks.Controller | |||||
PacSecret = ""; | PacSecret = ""; | ||||
} | } | ||||
PacUrl = $"http://{config.localHost}:{config.localPort}/pac?t={GetTimestamp(DateTime.Now)}{PacSecret}"; | |||||
PacUrl = $"http://{config.localHost}:{config.localPort}/{RESOURCE_NAME}?t={GetTimestamp(DateTime.Now)}{PacSecret}"; | |||||
} | } | ||||
@@ -66,15 +59,61 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
return false; | return false; | ||||
} | } | ||||
try | try | ||||
{ | { | ||||
/* | |||||
* RFC 7230 | |||||
* | |||||
GET /hello.txt HTTP/1.1 | |||||
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3 | |||||
Host: www.example.com | |||||
Accept-Language: en, mi | |||||
*/ | |||||
string request = Encoding.UTF8.GetString(firstPacket, 0, length); | string request = Encoding.UTF8.GetString(firstPacket, 0, length); | ||||
string[] lines = request.Split('\r', '\n'); | string[] lines = request.Split('\r', '\n'); | ||||
bool hostMatch = false, pathMatch = false, useSocks = false; | bool hostMatch = false, pathMatch = false, useSocks = false; | ||||
bool secretMatch = PacSecret.IsNullOrEmpty(); | bool secretMatch = PacSecret.IsNullOrEmpty(); | ||||
foreach (string line in lines) | |||||
if (lines.Length < 2) // need at lease RequestLine + Host | |||||
{ | { | ||||
string[] kv = line.Split(new char[] { ':' }, 2); | |||||
return false; | |||||
} | |||||
// parse request line | |||||
string requestLine = lines[0]; | |||||
// GET /pac?t=yyyyMMddHHmmssfff&secret=foobar HTTP/1.1 | |||||
string[] requestItems = requestLine.Split(' '); | |||||
if (requestItems.Length == 3 && requestItems[0] == "GET") | |||||
{ | |||||
int index = requestItems[1].IndexOf('?'); | |||||
if (index < 0) | |||||
{ | |||||
index = requestItems[1].Length; | |||||
} | |||||
string resourceString = requestItems[1].Substring(0, index).Remove(0, 1); | |||||
if (string.Equals(resourceString, RESOURCE_NAME, StringComparison.OrdinalIgnoreCase)) | |||||
{ | |||||
pathMatch = true; | |||||
if (!secretMatch) | |||||
{ | |||||
string queryString = requestItems[1].Substring(index); | |||||
if (queryString.Contains(PacSecret)) | |||||
{ | |||||
secretMatch = true; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
// parse request header | |||||
for (int i = 1; i < lines.Length; i++) | |||||
{ | |||||
if (string.IsNullOrEmpty(lines[i])) | |||||
continue; | |||||
string[] kv = lines[i].Split(new char[] { ':' }, 2); | |||||
if (kv.Length == 2) | if (kv.Length == 2) | ||||
{ | { | ||||
if (kv[0] == "Host") | if (kv[0] == "Host") | ||||
@@ -93,21 +132,8 @@ namespace Shadowsocks.Controller | |||||
// } | // } | ||||
//} | //} | ||||
} | } | ||||
else if (kv.Length == 1) | |||||
{ | |||||
if (line.IndexOf("pac", StringComparison.Ordinal) >= 0) | |||||
{ | |||||
pathMatch = true; | |||||
} | |||||
if (!secretMatch) | |||||
{ | |||||
if (line.IndexOf(PacSecret, StringComparison.Ordinal) >= 0) | |||||
{ | |||||
secretMatch = true; | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
if (hostMatch && pathMatch) | if (hostMatch && pathMatch) | ||||
{ | { | ||||
if (!secretMatch) | if (!secretMatch) | ||||
@@ -128,43 +154,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
public string TouchPACFile() | |||||
{ | |||||
if (File.Exists(PAC_FILE)) | |||||
{ | |||||
return PAC_FILE; | |||||
} | |||||
else | |||||
{ | |||||
FileManager.UncompressFile(PAC_FILE, Resources.proxy_pac_txt); | |||||
return PAC_FILE; | |||||
} | |||||
} | |||||
internal string TouchUserRuleFile() | |||||
{ | |||||
if (File.Exists(USER_RULE_FILE)) | |||||
{ | |||||
return USER_RULE_FILE; | |||||
} | |||||
else | |||||
{ | |||||
File.WriteAllText(USER_RULE_FILE, Resources.user_rule); | |||||
return USER_RULE_FILE; | |||||
} | |||||
} | |||||
private string GetPACContent() | |||||
{ | |||||
if (File.Exists(PAC_FILE)) | |||||
{ | |||||
return File.ReadAllText(PAC_FILE, Encoding.UTF8); | |||||
} | |||||
else | |||||
{ | |||||
return Utils.UnGzip(Resources.proxy_pac_txt); | |||||
} | |||||
} | |||||
public void SendResponse(Socket socket, bool useSocks) | public void SendResponse(Socket socket, bool useSocks) | ||||
{ | { | ||||
@@ -174,7 +164,7 @@ namespace Shadowsocks.Controller | |||||
string proxy = GetPACAddress(localEndPoint, useSocks); | string proxy = GetPACAddress(localEndPoint, useSocks); | ||||
string pacContent = GetPACContent().Replace("__PROXY__", proxy); | |||||
string pacContent = _pacDaemon.GetPACContent().Replace("__PROXY__", proxy); | |||||
string responseHead = String.Format(@"HTTP/1.1 200 OK | string responseHead = String.Format(@"HTTP/1.1 200 OK | ||||
Server: Shadowsocks | Server: Shadowsocks | ||||
@@ -205,66 +195,6 @@ Connection: Close | |||||
{ } | { } | ||||
} | } | ||||
private void WatchPacFile() | |||||
{ | |||||
PACFileWatcher?.Dispose(); | |||||
PACFileWatcher = new FileSystemWatcher(Directory.GetCurrentDirectory()); | |||||
PACFileWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; | |||||
PACFileWatcher.Filter = PAC_FILE; | |||||
PACFileWatcher.Changed += PACFileWatcher_Changed; | |||||
PACFileWatcher.Created += PACFileWatcher_Changed; | |||||
PACFileWatcher.Deleted += PACFileWatcher_Changed; | |||||
PACFileWatcher.Renamed += PACFileWatcher_Changed; | |||||
PACFileWatcher.EnableRaisingEvents = true; | |||||
} | |||||
private void WatchUserRuleFile() | |||||
{ | |||||
UserRuleFileWatcher?.Dispose(); | |||||
UserRuleFileWatcher = new FileSystemWatcher(Directory.GetCurrentDirectory()); | |||||
UserRuleFileWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; | |||||
UserRuleFileWatcher.Filter = USER_RULE_FILE; | |||||
UserRuleFileWatcher.Changed += UserRuleFileWatcher_Changed; | |||||
UserRuleFileWatcher.Created += UserRuleFileWatcher_Changed; | |||||
UserRuleFileWatcher.Deleted += UserRuleFileWatcher_Changed; | |||||
UserRuleFileWatcher.Renamed += UserRuleFileWatcher_Changed; | |||||
UserRuleFileWatcher.EnableRaisingEvents = true; | |||||
} | |||||
#region FileSystemWatcher.OnChanged() | |||||
// FileSystemWatcher Changed event is raised twice | |||||
// http://stackoverflow.com/questions/1764809/filesystemwatcher-changed-event-is-raised-twice | |||||
// Add a short delay to avoid raise event twice in a short period | |||||
private void PACFileWatcher_Changed(object sender, FileSystemEventArgs e) | |||||
{ | |||||
if (PACFileChanged != null) | |||||
{ | |||||
Logging.Info($"Detected: PAC file '{e.Name}' was {e.ChangeType.ToString().ToLower()}."); | |||||
Task.Factory.StartNew(() => | |||||
{ | |||||
((FileSystemWatcher)sender).EnableRaisingEvents = false; | |||||
System.Threading.Thread.Sleep(10); | |||||
PACFileChanged(this, new EventArgs()); | |||||
((FileSystemWatcher)sender).EnableRaisingEvents = true; | |||||
}); | |||||
} | |||||
} | |||||
private void UserRuleFileWatcher_Changed(object sender, FileSystemEventArgs e) | |||||
{ | |||||
if (UserRuleFileChanged != null) | |||||
{ | |||||
Logging.Info($"Detected: User Rule file '{e.Name}' was {e.ChangeType.ToString().ToLower()}."); | |||||
Task.Factory.StartNew(() => | |||||
{ | |||||
((FileSystemWatcher)sender).EnableRaisingEvents = false; | |||||
System.Threading.Thread.Sleep(10); | |||||
UserRuleFileChanged(this, new EventArgs()); | |||||
((FileSystemWatcher)sender).EnableRaisingEvents = true; | |||||
}); | |||||
} | |||||
} | |||||
#endregion | |||||
private string GetPACAddress(IPEndPoint localEndPoint, bool useSocks) | private string GetPACAddress(IPEndPoint localEndPoint, bool useSocks) | ||||
{ | { | ||||
@@ -28,6 +28,7 @@ namespace Shadowsocks.Controller | |||||
private Thread _trafficThread; | private Thread _trafficThread; | ||||
private Listener _listener; | private Listener _listener; | ||||
private PACDaemon _pacDaemon; | |||||
private PACServer _pacServer; | private PACServer _pacServer; | ||||
private Configuration _config; | private Configuration _config; | ||||
private StrategyManager _strategyManager; | private StrategyManager _strategyManager; | ||||
@@ -299,14 +300,14 @@ namespace Shadowsocks.Controller | |||||
public void TouchPACFile() | public void TouchPACFile() | ||||
{ | { | ||||
string pacFilename = _pacServer.TouchPACFile(); | |||||
string pacFilename = _pacDaemon.TouchPACFile(); | |||||
PACFileReadyToOpen?.Invoke(this, new PathEventArgs() { Path = pacFilename }); | PACFileReadyToOpen?.Invoke(this, new PathEventArgs() { Path = pacFilename }); | ||||
} | } | ||||
public void TouchUserRuleFile() | public void TouchUserRuleFile() | ||||
{ | { | ||||
string userRuleFilename = _pacServer.TouchUserRuleFile(); | |||||
string userRuleFilename = _pacDaemon.TouchUserRuleFile(); | |||||
UserRuleFileReadyToOpen?.Invoke(this, new PathEventArgs() { Path = userRuleFilename }); | UserRuleFileReadyToOpen?.Invoke(this, new PathEventArgs() { Path = userRuleFilename }); | ||||
} | } | ||||
@@ -468,13 +469,20 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
privoxyRunner = new PrivoxyRunner(); | privoxyRunner = new PrivoxyRunner(); | ||||
} | } | ||||
if (_pacDaemon == null) | |||||
{ | |||||
_pacDaemon = new PACDaemon(); | |||||
_pacDaemon.PACFileChanged += PacDaemon_PACFileChanged; | |||||
_pacDaemon.UserRuleFileChanged += PacDaemon_UserRuleFileChanged; | |||||
} | |||||
if (_pacServer == null) | if (_pacServer == null) | ||||
{ | { | ||||
_pacServer = new PACServer(); | |||||
_pacServer.PACFileChanged += PacServer_PACFileChanged; | |||||
_pacServer.UserRuleFileChanged += PacServer_UserRuleFileChanged; | |||||
_pacServer = new PACServer(_pacDaemon); | |||||
} | } | ||||
_pacServer.UpdateConfiguration(_config); | |||||
_pacServer.UpdatePACURL(_config); | |||||
if (gfwListUpdater == null) | if (gfwListUpdater == null) | ||||
{ | { | ||||
gfwListUpdater = new GFWListUpdater(); | gfwListUpdater = new GFWListUpdater(); | ||||
@@ -561,7 +569,7 @@ namespace Shadowsocks.Controller | |||||
SystemProxy.Update(_config, false, _pacServer); | SystemProxy.Update(_config, false, _pacServer); | ||||
} | } | ||||
private void PacServer_PACFileChanged(object sender, EventArgs e) | |||||
private void PacDaemon_PACFileChanged(object sender, EventArgs e) | |||||
{ | { | ||||
UpdateSystemProxy(); | UpdateSystemProxy(); | ||||
} | } | ||||
@@ -577,7 +585,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
private static readonly IEnumerable<char> IgnoredLineBegins = new[] { '!', '[' }; | private static readonly IEnumerable<char> IgnoredLineBegins = new[] { '!', '[' }; | ||||
private void PacServer_UserRuleFileChanged(object sender, EventArgs e) | |||||
private void PacDaemon_UserRuleFileChanged(object sender, EventArgs e) | |||||
{ | { | ||||
if (!File.Exists(Utils.GetTempPath("gfwlist.txt"))) | if (!File.Exists(Utils.GetTempPath("gfwlist.txt"))) | ||||
{ | { | ||||
@@ -0,0 +1,792 @@ | |||||
// Generated by gfwlist2pac in precise mode | |||||
// https://github.com/clowwindy/gfwlist2pac | |||||
// 2019-02-08: Updated to support shadowsocks-windows user rules. | |||||
var proxy = "__PROXY__"; | |||||
var userrules = __USERRULES__; | |||||
var rules = __RULES__; | |||||
/* | |||||
* This file is part of Adblock Plus <http://adblockplus.org/>, | |||||
* Copyright (C) 2006-2014 Eyeo GmbH | |||||
* | |||||
* Adblock Plus is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License version 3 as | |||||
* published by the Free Software Foundation. | |||||
* | |||||
* Adblock Plus is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
function createDict() | |||||
{ | |||||
var result = {}; | |||||
result.__proto__ = null; | |||||
return result; | |||||
} | |||||
function getOwnPropertyDescriptor(obj, key) | |||||
{ | |||||
if (obj.hasOwnProperty(key)) | |||||
{ | |||||
return obj[key]; | |||||
} | |||||
return null; | |||||
} | |||||
function extend(subclass, superclass, definition) | |||||
{ | |||||
if (Object.__proto__) | |||||
{ | |||||
definition.__proto__ = superclass.prototype; | |||||
subclass.prototype = definition; | |||||
} | |||||
else | |||||
{ | |||||
var tmpclass = function(){}, ret; | |||||
tmpclass.prototype = superclass.prototype; | |||||
subclass.prototype = new tmpclass(); | |||||
subclass.prototype.constructor = superclass; | |||||
for (var i in definition) | |||||
{ | |||||
if (definition.hasOwnProperty(i)) | |||||
{ | |||||
subclass.prototype[i] = definition[i]; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
function Filter(text) | |||||
{ | |||||
this.text = text; | |||||
this.subscriptions = []; | |||||
} | |||||
Filter.prototype = { | |||||
text: null, | |||||
subscriptions: null, | |||||
toString: function() | |||||
{ | |||||
return this.text; | |||||
} | |||||
}; | |||||
Filter.knownFilters = createDict(); | |||||
Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/; | |||||
Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/; | |||||
Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/; | |||||
Filter.fromText = function(text) | |||||
{ | |||||
if (text in Filter.knownFilters) | |||||
{ | |||||
return Filter.knownFilters[text]; | |||||
} | |||||
var ret; | |||||
if (text.charAt(0) == "!") | |||||
{ | |||||
ret = new CommentFilter(text); | |||||
} | |||||
else | |||||
{ | |||||
ret = RegExpFilter.fromText(text); | |||||
} | |||||
Filter.knownFilters[ret.text] = ret; | |||||
return ret; | |||||
}; | |||||
function InvalidFilter(text, reason) | |||||
{ | |||||
Filter.call(this, text); | |||||
this.reason = reason; | |||||
} | |||||
extend(InvalidFilter, Filter, { | |||||
reason: null | |||||
}); | |||||
function CommentFilter(text) | |||||
{ | |||||
Filter.call(this, text); | |||||
} | |||||
extend(CommentFilter, Filter, { | |||||
}); | |||||
function ActiveFilter(text, domains) | |||||
{ | |||||
Filter.call(this, text); | |||||
this.domainSource = domains; | |||||
} | |||||
extend(ActiveFilter, Filter, { | |||||
domainSource: null, | |||||
domainSeparator: null, | |||||
ignoreTrailingDot: true, | |||||
domainSourceIsUpperCase: false, | |||||
getDomains: function() | |||||
{ | |||||
var prop = getOwnPropertyDescriptor(this, "domains"); | |||||
if (prop) | |||||
{ | |||||
return prop; | |||||
} | |||||
var domains = null; | |||||
if (this.domainSource) | |||||
{ | |||||
var source = this.domainSource; | |||||
if (!this.domainSourceIsUpperCase) | |||||
{ | |||||
source = source.toUpperCase(); | |||||
} | |||||
var list = source.split(this.domainSeparator); | |||||
if (list.length == 1 && (list[0]).charAt(0) != "~") | |||||
{ | |||||
domains = createDict(); | |||||
domains[""] = false; | |||||
if (this.ignoreTrailingDot) | |||||
{ | |||||
list[0] = list[0].replace(/\.+$/, ""); | |||||
} | |||||
domains[list[0]] = true; | |||||
} | |||||
else | |||||
{ | |||||
var hasIncludes = false; | |||||
for (var i = 0; i < list.length; i++) | |||||
{ | |||||
var domain = list[i]; | |||||
if (this.ignoreTrailingDot) | |||||
{ | |||||
domain = domain.replace(/\.+$/, ""); | |||||
} | |||||
if (domain == "") | |||||
{ | |||||
continue; | |||||
} | |||||
var include; | |||||
if (domain.charAt(0) == "~") | |||||
{ | |||||
include = false; | |||||
domain = domain.substr(1); | |||||
} | |||||
else | |||||
{ | |||||
include = true; | |||||
hasIncludes = true; | |||||
} | |||||
if (!domains) | |||||
{ | |||||
domains = createDict(); | |||||
} | |||||
domains[domain] = include; | |||||
} | |||||
domains[""] = !hasIncludes; | |||||
} | |||||
this.domainSource = null; | |||||
} | |||||
return this.domains; | |||||
}, | |||||
sitekeys: null, | |||||
isActiveOnDomain: function(docDomain, sitekey) | |||||
{ | |||||
if (this.getSitekeys() && (!sitekey || this.getSitekeys().indexOf(sitekey.toUpperCase()) < 0)) | |||||
{ | |||||
return false; | |||||
} | |||||
if (!this.getDomains()) | |||||
{ | |||||
return true; | |||||
} | |||||
if (!docDomain) | |||||
{ | |||||
return this.getDomains()[""]; | |||||
} | |||||
if (this.ignoreTrailingDot) | |||||
{ | |||||
docDomain = docDomain.replace(/\.+$/, ""); | |||||
} | |||||
docDomain = docDomain.toUpperCase(); | |||||
while (true) | |||||
{ | |||||
if (docDomain in this.getDomains()) | |||||
{ | |||||
return this.domains[docDomain]; | |||||
} | |||||
var nextDot = docDomain.indexOf("."); | |||||
if (nextDot < 0) | |||||
{ | |||||
break; | |||||
} | |||||
docDomain = docDomain.substr(nextDot + 1); | |||||
} | |||||
return this.domains[""]; | |||||
}, | |||||
isActiveOnlyOnDomain: function(docDomain) | |||||
{ | |||||
if (!docDomain || !this.getDomains() || this.getDomains()[""]) | |||||
{ | |||||
return false; | |||||
} | |||||
if (this.ignoreTrailingDot) | |||||
{ | |||||
docDomain = docDomain.replace(/\.+$/, ""); | |||||
} | |||||
docDomain = docDomain.toUpperCase(); | |||||
for (var domain in this.getDomains()) | |||||
{ | |||||
if (this.domains[domain] && domain != docDomain && (domain.length <= docDomain.length || domain.indexOf("." + docDomain) != domain.length - docDomain.length - 1)) | |||||
{ | |||||
return false; | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
}); | |||||
function RegExpFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) | |||||
{ | |||||
ActiveFilter.call(this, text, domains, sitekeys); | |||||
if (contentType != null) | |||||
{ | |||||
this.contentType = contentType; | |||||
} | |||||
if (matchCase) | |||||
{ | |||||
this.matchCase = matchCase; | |||||
} | |||||
if (thirdParty != null) | |||||
{ | |||||
this.thirdParty = thirdParty; | |||||
} | |||||
if (sitekeys != null) | |||||
{ | |||||
this.sitekeySource = sitekeys; | |||||
} | |||||
if (regexpSource.length >= 2 && regexpSource.charAt(0) == "/" && regexpSource.charAt(regexpSource.length - 1) == "/") | |||||
{ | |||||
var regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), this.matchCase ? "" : "i"); | |||||
this.regexp = regexp; | |||||
} | |||||
else | |||||
{ | |||||
this.regexpSource = regexpSource; | |||||
} | |||||
} | |||||
extend(RegExpFilter, ActiveFilter, { | |||||
domainSourceIsUpperCase: true, | |||||
length: 1, | |||||
domainSeparator: "|", | |||||
regexpSource: null, | |||||
getRegexp: function() | |||||
{ | |||||
var prop = getOwnPropertyDescriptor(this, "regexp"); | |||||
if (prop) | |||||
{ | |||||
return prop; | |||||
} | |||||
var source = this.regexpSource.replace(/\*+/g, "*").replace(/\^\|$/, "^").replace(/\W/g, "\\$&").replace(/\\\*/g, ".*").replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x60\\x7B-\\x7F]|$)").replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?").replace(/^\\\|/, "^").replace(/\\\|$/, "$").replace(/^(\.\*)/, "").replace(/(\.\*)$/, ""); | |||||
var regexp = new RegExp(source, this.matchCase ? "" : "i"); | |||||
this.regexp = regexp; | |||||
return regexp; | |||||
}, | |||||
contentType: 2147483647, | |||||
matchCase: false, | |||||
thirdParty: null, | |||||
sitekeySource: null, | |||||
getSitekeys: function() | |||||
{ | |||||
var prop = getOwnPropertyDescriptor(this, "sitekeys"); | |||||
if (prop) | |||||
{ | |||||
return prop; | |||||
} | |||||
var sitekeys = null; | |||||
if (this.sitekeySource) | |||||
{ | |||||
sitekeys = this.sitekeySource.split("|"); | |||||
this.sitekeySource = null; | |||||
} | |||||
this.sitekeys = sitekeys; | |||||
return this.sitekeys; | |||||
}, | |||||
matches: function(location, contentType, docDomain, thirdParty, sitekey) | |||||
{ | |||||
if (this.getRegexp().test(location) && this.isActiveOnDomain(docDomain, sitekey)) | |||||
{ | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
}); | |||||
RegExpFilter.prototype["0"] = "#this"; | |||||
RegExpFilter.fromText = function(text) | |||||
{ | |||||
var blocking = true; | |||||
var origText = text; | |||||
if (text.indexOf("@@") == 0) | |||||
{ | |||||
blocking = false; | |||||
text = text.substr(2); | |||||
} | |||||
var contentType = null; | |||||
var matchCase = null; | |||||
var domains = null; | |||||
var sitekeys = null; | |||||
var thirdParty = null; | |||||
var collapse = null; | |||||
var options; | |||||
var match = text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null; | |||||
if (match) | |||||
{ | |||||
options = match[1].toUpperCase().split(","); | |||||
text = match.input.substr(0, match.index); | |||||
for (var _loopIndex6 = 0; _loopIndex6 < options.length; ++_loopIndex6) | |||||
{ | |||||
var option = options[_loopIndex6]; | |||||
var value = null; | |||||
var separatorIndex = option.indexOf("="); | |||||
if (separatorIndex >= 0) | |||||
{ | |||||
value = option.substr(separatorIndex + 1); | |||||
option = option.substr(0, separatorIndex); | |||||
} | |||||
option = option.replace(/-/, "_"); | |||||
if (option in RegExpFilter.typeMap) | |||||
{ | |||||
if (contentType == null) | |||||
{ | |||||
contentType = 0; | |||||
} | |||||
contentType |= RegExpFilter.typeMap[option]; | |||||
} | |||||
else if (option.charAt(0) == "~" && option.substr(1) in RegExpFilter.typeMap) | |||||
{ | |||||
if (contentType == null) | |||||
{ | |||||
contentType = RegExpFilter.prototype.contentType; | |||||
} | |||||
contentType &= ~RegExpFilter.typeMap[option.substr(1)]; | |||||
} | |||||
else if (option == "MATCH_CASE") | |||||
{ | |||||
matchCase = true; | |||||
} | |||||
else if (option == "~MATCH_CASE") | |||||
{ | |||||
matchCase = false; | |||||
} | |||||
else if (option == "DOMAIN" && typeof value != "undefined") | |||||
{ | |||||
domains = value; | |||||
} | |||||
else if (option == "THIRD_PARTY") | |||||
{ | |||||
thirdParty = true; | |||||
} | |||||
else if (option == "~THIRD_PARTY") | |||||
{ | |||||
thirdParty = false; | |||||
} | |||||
else if (option == "COLLAPSE") | |||||
{ | |||||
collapse = true; | |||||
} | |||||
else if (option == "~COLLAPSE") | |||||
{ | |||||
collapse = false; | |||||
} | |||||
else if (option == "SITEKEY" && typeof value != "undefined") | |||||
{ | |||||
sitekeys = value; | |||||
} | |||||
else | |||||
{ | |||||
return new InvalidFilter(origText, "Unknown option " + option.toLowerCase()); | |||||
} | |||||
} | |||||
} | |||||
if (!blocking && (contentType == null || contentType & RegExpFilter.typeMap.DOCUMENT) && (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text)) | |||||
{ | |||||
if (contentType == null) | |||||
{ | |||||
contentType = RegExpFilter.prototype.contentType; | |||||
} | |||||
contentType &= ~RegExpFilter.typeMap.DOCUMENT; | |||||
} | |||||
try | |||||
{ | |||||
if (blocking) | |||||
{ | |||||
return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse); | |||||
} | |||||
else | |||||
{ | |||||
return new WhitelistFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys); | |||||
} | |||||
} | |||||
catch (e) | |||||
{ | |||||
return new InvalidFilter(origText, e); | |||||
} | |||||
}; | |||||
RegExpFilter.typeMap = { | |||||
OTHER: 1, | |||||
SCRIPT: 2, | |||||
IMAGE: 4, | |||||
STYLESHEET: 8, | |||||
OBJECT: 16, | |||||
SUBDOCUMENT: 32, | |||||
DOCUMENT: 64, | |||||
XBL: 1, | |||||
PING: 1, | |||||
XMLHTTPREQUEST: 2048, | |||||
OBJECT_SUBREQUEST: 4096, | |||||
DTD: 1, | |||||
MEDIA: 16384, | |||||
FONT: 32768, | |||||
BACKGROUND: 4, | |||||
POPUP: 268435456, | |||||
ELEMHIDE: 1073741824 | |||||
}; | |||||
RegExpFilter.prototype.contentType &= ~ (RegExpFilter.typeMap.ELEMHIDE | RegExpFilter.typeMap.POPUP); | |||||
function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys, collapse) | |||||
{ | |||||
RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); | |||||
this.collapse = collapse; | |||||
} | |||||
extend(BlockingFilter, RegExpFilter, { | |||||
collapse: null | |||||
}); | |||||
function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) | |||||
{ | |||||
RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); | |||||
} | |||||
extend(WhitelistFilter, RegExpFilter, { | |||||
}); | |||||
function Matcher() | |||||
{ | |||||
this.clear(); | |||||
} | |||||
Matcher.prototype = { | |||||
filterByKeyword: null, | |||||
keywordByFilter: null, | |||||
clear: function() | |||||
{ | |||||
this.filterByKeyword = createDict(); | |||||
this.keywordByFilter = createDict(); | |||||
}, | |||||
add: function(filter) | |||||
{ | |||||
if (filter.text in this.keywordByFilter) | |||||
{ | |||||
return; | |||||
} | |||||
var keyword = this.findKeyword(filter); | |||||
var oldEntry = this.filterByKeyword[keyword]; | |||||
if (typeof oldEntry == "undefined") | |||||
{ | |||||
this.filterByKeyword[keyword] = filter; | |||||
} | |||||
else if (oldEntry.length == 1) | |||||
{ | |||||
this.filterByKeyword[keyword] = [oldEntry, filter]; | |||||
} | |||||
else | |||||
{ | |||||
oldEntry.push(filter); | |||||
} | |||||
this.keywordByFilter[filter.text] = keyword; | |||||
}, | |||||
remove: function(filter) | |||||
{ | |||||
if (!(filter.text in this.keywordByFilter)) | |||||
{ | |||||
return; | |||||
} | |||||
var keyword = this.keywordByFilter[filter.text]; | |||||
var list = this.filterByKeyword[keyword]; | |||||
if (list.length <= 1) | |||||
{ | |||||
delete this.filterByKeyword[keyword]; | |||||
} | |||||
else | |||||
{ | |||||
var index = list.indexOf(filter); | |||||
if (index >= 0) | |||||
{ | |||||
list.splice(index, 1); | |||||
if (list.length == 1) | |||||
{ | |||||
this.filterByKeyword[keyword] = list[0]; | |||||
} | |||||
} | |||||
} | |||||
delete this.keywordByFilter[filter.text]; | |||||
}, | |||||
findKeyword: function(filter) | |||||
{ | |||||
var result = ""; | |||||
var text = filter.text; | |||||
if (Filter.regexpRegExp.test(text)) | |||||
{ | |||||
return result; | |||||
} | |||||
var match = Filter.optionsRegExp.exec(text); | |||||
if (match) | |||||
{ | |||||
text = match.input.substr(0, match.index); | |||||
} | |||||
if (text.substr(0, 2) == "@@") | |||||
{ | |||||
text = text.substr(2); | |||||
} | |||||
var candidates = text.toLowerCase().match(/[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g); | |||||
if (!candidates) | |||||
{ | |||||
return result; | |||||
} | |||||
var hash = this.filterByKeyword; | |||||
var resultCount = 16777215; | |||||
var resultLength = 0; | |||||
for (var i = 0, l = candidates.length; i < l; i++) | |||||
{ | |||||
var candidate = candidates[i].substr(1); | |||||
var count = candidate in hash ? hash[candidate].length : 0; | |||||
if (count < resultCount || count == resultCount && candidate.length > resultLength) | |||||
{ | |||||
result = candidate; | |||||
resultCount = count; | |||||
resultLength = candidate.length; | |||||
} | |||||
} | |||||
return result; | |||||
}, | |||||
hasFilter: function(filter) | |||||
{ | |||||
return filter.text in this.keywordByFilter; | |||||
}, | |||||
getKeywordForFilter: function(filter) | |||||
{ | |||||
if (filter.text in this.keywordByFilter) | |||||
{ | |||||
return this.keywordByFilter[filter.text]; | |||||
} | |||||
else | |||||
{ | |||||
return null; | |||||
} | |||||
}, | |||||
_checkEntryMatch: function(keyword, location, contentType, docDomain, thirdParty, sitekey) | |||||
{ | |||||
var list = this.filterByKeyword[keyword]; | |||||
for (var i = 0; i < list.length; i++) | |||||
{ | |||||
var filter = list[i]; | |||||
if (filter == "#this") | |||||
{ | |||||
filter = list; | |||||
} | |||||
if (filter.matches(location, contentType, docDomain, thirdParty, sitekey)) | |||||
{ | |||||
return filter; | |||||
} | |||||
} | |||||
return null; | |||||
}, | |||||
matchesAny: function(location, contentType, docDomain, thirdParty, sitekey) | |||||
{ | |||||
var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); | |||||
if (candidates === null) | |||||
{ | |||||
candidates = []; | |||||
} | |||||
candidates.push(""); | |||||
for (var i = 0, l = candidates.length; i < l; i++) | |||||
{ | |||||
var substr = candidates[i]; | |||||
if (substr in this.filterByKeyword) | |||||
{ | |||||
var result = this._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); | |||||
if (result) | |||||
{ | |||||
return result; | |||||
} | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
}; | |||||
function CombinedMatcher() | |||||
{ | |||||
this.blacklist = new Matcher(); | |||||
this.whitelist = new Matcher(); | |||||
this.resultCache = createDict(); | |||||
} | |||||
CombinedMatcher.maxCacheEntries = 1000; | |||||
CombinedMatcher.prototype = { | |||||
blacklist: null, | |||||
whitelist: null, | |||||
resultCache: null, | |||||
cacheEntries: 0, | |||||
clear: function() | |||||
{ | |||||
this.blacklist.clear(); | |||||
this.whitelist.clear(); | |||||
this.resultCache = createDict(); | |||||
this.cacheEntries = 0; | |||||
}, | |||||
add: function(filter) | |||||
{ | |||||
if (filter instanceof WhitelistFilter) | |||||
{ | |||||
this.whitelist.add(filter); | |||||
} | |||||
else | |||||
{ | |||||
this.blacklist.add(filter); | |||||
} | |||||
if (this.cacheEntries > 0) | |||||
{ | |||||
this.resultCache = createDict(); | |||||
this.cacheEntries = 0; | |||||
} | |||||
}, | |||||
remove: function(filter) | |||||
{ | |||||
if (filter instanceof WhitelistFilter) | |||||
{ | |||||
this.whitelist.remove(filter); | |||||
} | |||||
else | |||||
{ | |||||
this.blacklist.remove(filter); | |||||
} | |||||
if (this.cacheEntries > 0) | |||||
{ | |||||
this.resultCache = createDict(); | |||||
this.cacheEntries = 0; | |||||
} | |||||
}, | |||||
findKeyword: function(filter) | |||||
{ | |||||
if (filter instanceof WhitelistFilter) | |||||
{ | |||||
return this.whitelist.findKeyword(filter); | |||||
} | |||||
else | |||||
{ | |||||
return this.blacklist.findKeyword(filter); | |||||
} | |||||
}, | |||||
hasFilter: function(filter) | |||||
{ | |||||
if (filter instanceof WhitelistFilter) | |||||
{ | |||||
return this.whitelist.hasFilter(filter); | |||||
} | |||||
else | |||||
{ | |||||
return this.blacklist.hasFilter(filter); | |||||
} | |||||
}, | |||||
getKeywordForFilter: function(filter) | |||||
{ | |||||
if (filter instanceof WhitelistFilter) | |||||
{ | |||||
return this.whitelist.getKeywordForFilter(filter); | |||||
} | |||||
else | |||||
{ | |||||
return this.blacklist.getKeywordForFilter(filter); | |||||
} | |||||
}, | |||||
isSlowFilter: function(filter) | |||||
{ | |||||
var matcher = filter instanceof WhitelistFilter ? this.whitelist : this.blacklist; | |||||
if (matcher.hasFilter(filter)) | |||||
{ | |||||
return !matcher.getKeywordForFilter(filter); | |||||
} | |||||
else | |||||
{ | |||||
return !matcher.findKeyword(filter); | |||||
} | |||||
}, | |||||
matchesAnyInternal: function(location, contentType, docDomain, thirdParty, sitekey) | |||||
{ | |||||
var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); | |||||
if (candidates === null) | |||||
{ | |||||
candidates = []; | |||||
} | |||||
candidates.push(""); | |||||
var blacklistHit = null; | |||||
for (var i = 0, l = candidates.length; i < l; i++) | |||||
{ | |||||
var substr = candidates[i]; | |||||
if (substr in this.whitelist.filterByKeyword) | |||||
{ | |||||
var result = this.whitelist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); | |||||
if (result) | |||||
{ | |||||
return result; | |||||
} | |||||
} | |||||
if (substr in this.blacklist.filterByKeyword && blacklistHit === null) | |||||
{ | |||||
blacklistHit = this.blacklist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); | |||||
} | |||||
} | |||||
return blacklistHit; | |||||
}, | |||||
matchesAny: function(location, docDomain) | |||||
{ | |||||
var key = location + " " + docDomain + " "; | |||||
if (key in this.resultCache) | |||||
{ | |||||
return this.resultCache[key]; | |||||
} | |||||
var result = this.matchesAnyInternal(location, 0, docDomain, null, null); | |||||
if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) | |||||
{ | |||||
this.resultCache = createDict(); | |||||
this.cacheEntries = 0; | |||||
} | |||||
this.resultCache[key] = result; | |||||
this.cacheEntries++; | |||||
return result; | |||||
} | |||||
}; | |||||
var userrulesMatcher = new CombinedMatcher(); | |||||
var defaultMatcher = new CombinedMatcher(); | |||||
var direct = 'DIRECT;'; | |||||
for (var i = 0; i < userrules.length; i++) { | |||||
userrulesMatcher.add(Filter.fromText(userrules[i])); | |||||
} | |||||
for (var i = 0; i < rules.length; i++) { | |||||
defaultMatcher.add(Filter.fromText(rules[i])); | |||||
} | |||||
function FindProxyForURL(url, host) { | |||||
if (userrulesMatcher.matchesAny(url, host) instanceof BlockingFilter) { | |||||
return proxy; | |||||
} | |||||
if (userrulesMatcher.matchesAny(url, host) instanceof WhitelistFilter) { | |||||
return direct; | |||||
} | |||||
if (defaultMatcher.matchesAny(url, host) instanceof BlockingFilter) { | |||||
return proxy; | |||||
} | |||||
return direct; | |||||
} |
@@ -61,12 +61,27 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Looks up a localized resource of type System.Byte[]. | |||||
/// Looks up a localized string similar to // Generated by gfwlist2pac in precise mode | |||||
///// https://github.com/clowwindy/gfwlist2pac | |||||
/// | |||||
///// 2019-02-08: Updated to support shadowsocks-windows user rules. | |||||
/// | |||||
///var proxy = "__PROXY__"; | |||||
/// | |||||
///var userrules = __USERRULES__; | |||||
///var rules = __RULES__; | |||||
/// | |||||
////* | |||||
///* This file is part of Adblock Plus <http://adblockplus.org/>, | |||||
///* Copyright (C) 2006-2014 Eyeo GmbH | |||||
///* | |||||
///* Adblock Plus is free software: you can redistribute it and/or modify | |||||
///* it under the terms of the GNU General Public License version 3 as | |||||
///* published by t [rest of string was truncated]";. | |||||
/// </summary> | /// </summary> | ||||
internal static byte[] abp_js { | |||||
internal static string abp_js { | |||||
get { | get { | ||||
object obj = ResourceManager.GetObject("abp_js", resourceCulture); | |||||
return ((byte[])(obj)); | |||||
return ResourceManager.GetString("abp_js", resourceCulture); | |||||
} | } | ||||
} | } | ||||
@@ -138,12 +153,33 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Looks up a localized resource of type System.Byte[]. | |||||
/// Looks up a localized string similar to // Generated by gfwlist2pac in precise mode | |||||
///// https://github.com/clowwindy/gfwlist2pac | |||||
/// | |||||
///// 2019-02-08: Updated to support shadowsocks-windows user rules. | |||||
/// | |||||
///var proxy = "__PROXY__"; | |||||
/// | |||||
///var userrules = []; | |||||
///var rules = [ | |||||
/// "|http://85.17.73.31/", | |||||
/// "||agnesb.fr", | |||||
/// "||akiba-web.com", | |||||
/// "||altrec.com", | |||||
/// "||angela-merkel.de", | |||||
/// "||angola.org", | |||||
/// "||apartmentratings.com", | |||||
/// "||apartments.com", | |||||
/// "||arena.taipei", | |||||
/// "||asianspiss.com", | |||||
/// "||assimp.org", | |||||
/// "||athenaeizou.com", | |||||
/// "||azubu.tv", | |||||
/// [rest of string was truncated]";. | |||||
/// </summary> | /// </summary> | ||||
internal static byte[] proxy_pac_txt { | |||||
internal static string proxy_pac_txt { | |||||
get { | get { | ||||
object obj = ResourceManager.GetObject("proxy_pac_txt", resourceCulture); | |||||
return ((byte[])(obj)); | |||||
return ResourceManager.GetString("proxy_pac_txt", resourceCulture); | |||||
} | } | ||||
} | } | ||||
@@ -119,7 +119,7 @@ | |||||
</resheader> | </resheader> | ||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> | <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> | ||||
<data name="abp_js" type="System.Resources.ResXFileRef, System.Windows.Forms"> | <data name="abp_js" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||
<value>..\Data\abp.js.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||||
<value>..\Data\abp.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;gb2312</value> | |||||
</data> | </data> | ||||
<data name="ja" type="System.Resources.ResXFileRef, System.Windows.Forms"> | <data name="ja" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||
<value>..\Data\ja.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value> | <value>..\Data\ja.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value> | ||||
@@ -134,7 +134,7 @@ | |||||
<value>..\data\privoxy.exe.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | <value>..\data\privoxy.exe.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | ||||
</data> | </data> | ||||
<data name="proxy_pac_txt" type="System.Resources.ResXFileRef, System.Windows.Forms"> | <data name="proxy_pac_txt" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||
<value>..\Data\proxy.pac.txt.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||||
<value>..\Data\proxy.pac.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value> | |||||
</data> | </data> | ||||
<data name="ss32Fill" type="System.Resources.ResXFileRef, System.Windows.Forms"> | <data name="ss32Fill" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||
<value>..\Resources\ss32Fill.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> | <value>..\Resources\ss32Fill.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> | ||||
@@ -103,6 +103,7 @@ | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<Compile Include="Controller\HotkeyReg.cs" /> | <Compile Include="Controller\HotkeyReg.cs" /> | ||||
<Compile Include="Controller\Service\PACDaemon.cs" /> | |||||
<Compile Include="Controller\System\Hotkeys\HotkeyCallbacks.cs" /> | <Compile Include="Controller\System\Hotkeys\HotkeyCallbacks.cs" /> | ||||
<Compile Include="Encryption\AEAD\AEADEncryptor.cs" /> | <Compile Include="Encryption\AEAD\AEADEncryptor.cs" /> | ||||
<Compile Include="Encryption\AEAD\AEADMbedTLSEncryptor.cs" /> | <Compile Include="Encryption\AEAD\AEADMbedTLSEncryptor.cs" /> | ||||
@@ -255,10 +256,8 @@ | |||||
<None Include="app.manifest"> | <None Include="app.manifest"> | ||||
<SubType>Designer</SubType> | <SubType>Designer</SubType> | ||||
</None> | </None> | ||||
<None Include="Data\abp.js.gz" /> | |||||
<None Include="Data\libsscrypto.dll.gz" /> | <None Include="Data\libsscrypto.dll.gz" /> | ||||
<None Include="Data\privoxy.exe.gz" /> | <None Include="Data\privoxy.exe.gz" /> | ||||
<None Include="Data\proxy.pac.txt.gz" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<None Include="Data\sysproxy.exe.gz" /> | <None Include="Data\sysproxy.exe.gz" /> | ||||
@@ -270,6 +269,8 @@ | |||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput> | <LastGenOutput>Settings.Designer.cs</LastGenOutput> | ||||
</None> | </None> | ||||
<None Include="Resources\ssw128.png" /> | <None Include="Resources\ssw128.png" /> | ||||
<Content Include="Data\abp.js" /> | |||||
<Content Include="Data\proxy.pac.txt" /> | |||||
<Content Include="Data\zh_CN.txt" /> | <Content Include="Data\zh_CN.txt" /> | ||||
<Content Include="Data\zh_TW.txt" /> | <Content Include="Data\zh_TW.txt" /> | ||||
<Content Include="Data\ja.txt" /> | <Content Include="Data\ja.txt" /> | ||||