Commit a84070c2 authored by Max Lv's avatar Max Lv

Allow adding URLs to custom rules. #1276

parent 398db4f4
...@@ -123,7 +123,7 @@ ...@@ -123,7 +123,7 @@
<string name="route_entry_bypass_lan_chn">"LAN 及び中国本土のアドレスを迂回する"</string> <string name="route_entry_bypass_lan_chn">"LAN 及び中国本土のアドレスを迂回する"</string>
<string name="route_entry_chinalist">"中国本土のアドレス以外を迂回する"</string> <string name="route_entry_chinalist">"中国本土のアドレス以外を迂回する"</string>
<string name="proxied_apps_summary_v21">"選択したアプリにプロキシを設定する"</string> <string name="proxied_apps_summary_v21">"選択したアプリにプロキシを設定する"</string>
<string name="acl_rule_templates_generic">"サブネット/ホスト名 PCRE パターン"</string> <string name="acl_rule_templates_generic">"URL/サブネット/ホスト名 PCRE パターン"</string>
<string name="acl_rule_templates_domain">"ドメイン及び全てのサブドメイン"</string> <string name="acl_rule_templates_domain">"ドメイン及び全てのサブドメイン"</string>
<!-- plugin --> <!-- plugin -->
......
...@@ -131,7 +131,7 @@ ...@@ -131,7 +131,7 @@
<string name="route_entry_bypass_lan_chn">"Все, кроме LAN и Китая"</string> <string name="route_entry_bypass_lan_chn">"Все, кроме LAN и Китая"</string>
<string name="route_entry_chinalist">"Список Китай"</string> <string name="route_entry_chinalist">"Список Китай"</string>
<string name="proxied_apps_summary_v21">"Установить прокси для выбранных приложений"</string> <string name="proxied_apps_summary_v21">"Установить прокси для выбранных приложений"</string>
<string name="acl_rule_templates_generic">"Подсеть/Регулярное выражение (PCRE) имени хоста"</string> <string name="acl_rule_templates_generic">"URL/Подсеть/Регулярное выражение (PCRE) имени хоста"</string>
<string name="acl_rule_templates_domain">"Доменное имя и все его поддомены"</string> <string name="acl_rule_templates_domain">"Доменное имя и все его поддомены"</string>
<!-- plugin --> <!-- plugin -->
......
...@@ -124,7 +124,7 @@ ...@@ -124,7 +124,7 @@
<string name="route_entry_bypass_lan_chn">"绕过局域网及中国大陆地址"</string> <string name="route_entry_bypass_lan_chn">"绕过局域网及中国大陆地址"</string>
<string name="route_entry_chinalist">"仅代理中国大陆地址"</string> <string name="route_entry_chinalist">"仅代理中国大陆地址"</string>
<string name="proxied_apps_summary_v21">"为应用程序分别设置代理"</string> <string name="proxied_apps_summary_v21">"为应用程序分别设置代理"</string>
<string name="acl_rule_templates_generic">"子网/域名 PCRE 正则表达式"</string> <string name="acl_rule_templates_generic">"URL/子网/域名 PCRE 正则表达式"</string>
<string name="acl_rule_templates_domain">"域名及其子域名"</string> <string name="acl_rule_templates_domain">"域名及其子域名"</string>
<!-- plugin --> <!-- plugin -->
......
...@@ -122,7 +122,7 @@ ...@@ -122,7 +122,7 @@
<string name="route_entry_bypass_lan_chn">"略過區域網路及中國大陸"</string> <string name="route_entry_bypass_lan_chn">"略過區域網路及中國大陸"</string>
<string name="route_entry_chinalist">"China List"</string> <string name="route_entry_chinalist">"China List"</string>
<string name="proxied_apps_summary_v21">"為已選擇的應用程式設定 Proxy"</string> <string name="proxied_apps_summary_v21">"為已選擇的應用程式設定 Proxy"</string>
<string name="acl_rule_templates_generic">"子網路/主機名稱 PCRE 模式"</string> <string name="acl_rule_templates_generic">"URL/子網路/主機名稱 PCRE 模式"</string>
<string name="acl_rule_templates_domain">"網域及其所有子網域"</string> <string name="acl_rule_templates_domain">"網域及其所有子網域"</string>
<!-- plugin --> <!-- plugin -->
......
...@@ -130,7 +130,7 @@ ...@@ -130,7 +130,7 @@
<string name="custom_rules">Custom rules</string> <string name="custom_rules">Custom rules</string>
<string name="action_selection">Selection…</string> <string name="action_selection">Selection…</string>
<string name="action_add_rule">Add rule(s)…</string> <string name="action_add_rule">Add rule(s)…</string>
<string name="acl_rule_templates_generic">Subnet/Hostname PCRE pattern</string> <string name="acl_rule_templates_generic">URL, Subnet or Hostname PCRE pattern</string>
<string name="acl_rule_templates_domain">Domain name and all its subdomain names</string> <string name="acl_rule_templates_domain">Domain name and all its subdomain names</string>
<string name="edit_rule">Edit rule</string> <string name="edit_rule">Edit rule</string>
......
...@@ -154,7 +154,7 @@ trait BaseService extends Service { ...@@ -154,7 +154,7 @@ trait BaseService extends Service {
} }
if (profile.route == Acl.CUSTOM_RULES) // rationalize custom rules if (profile.route == Acl.CUSTOM_RULES) // rationalize custom rules
Acl.save(Acl.CUSTOM_RULES, new Acl().fromId(Acl.CUSTOM_RULES)) Acl.save(Acl.CUSTOM_RULES, new Acl().fromId(Acl.CUSTOM_RULES), true)
plugin = new PluginConfiguration(profile.plugin).selectedOptions plugin = new PluginConfiguration(profile.plugin).selectedOptions
pluginPath = PluginManager.init(plugin) pluginPath = PluginManager.init(plugin)
......
package com.github.shadowsocks.acl package com.github.shadowsocks.acl
import java.io.{File, FileNotFoundException} import java.io.{File, FileNotFoundException, IOException}
import com.github.shadowsocks.ShadowsocksApplication.app import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.utils.IOUtils import com.github.shadowsocks.utils.IOUtils
...@@ -22,6 +22,7 @@ class Acl { ...@@ -22,6 +22,7 @@ class Acl {
val bypassHostnames = new mutable.SortedList[String]() val bypassHostnames = new mutable.SortedList[String]()
val proxyHostnames = new mutable.SortedList[String]() val proxyHostnames = new mutable.SortedList[String]()
val subnets = new mutable.SortedList[Subnet]() val subnets = new mutable.SortedList[Subnet]()
val urls = new mutable.SortedList[String]()
@DatabaseField @DatabaseField
var bypass: Boolean = _ var bypass: Boolean = _
...@@ -40,6 +41,10 @@ class Acl { ...@@ -40,6 +41,10 @@ class Acl {
subnets.clear() subnets.clear()
subnets ++= value.split("\n").map(Subnet.fromString) subnets ++= value.split("\n").map(Subnet.fromString)
} }
def setUrlRules(value: String) {
urls.clear()
urls ++= value.split("\n")
}
def fromAcl(other: Acl): Acl = { def fromAcl(other: Acl): Acl = {
bypassHostnames.clear() bypassHostnames.clear()
...@@ -48,21 +53,35 @@ class Acl { ...@@ -48,21 +53,35 @@ class Acl {
proxyHostnames ++= other.proxyHostnames proxyHostnames ++= other.proxyHostnames
subnets.clear() subnets.clear()
subnets ++= other.subnets subnets ++= other.subnets
urls.clear()
urls ++= other.urls
bypass = other.bypass bypass = other.bypass
this this
} }
def fromSource(value: Source, defaultBypass: Boolean = false): Acl = { def fromSource(value: Source, defaultBypass: Boolean = true): Acl = {
bypassHostnames.clear() bypassHostnames.clear()
proxyHostnames.clear() proxyHostnames.clear()
this.subnets.clear() this.subnets.clear()
this.urls.clear()
bypass = defaultBypass bypass = defaultBypass
lazy val bypassSubnets = new mutable.SortedList[Subnet]() lazy val bypassSubnets = new mutable.SortedList[Subnet]()
lazy val proxySubnets = new mutable.SortedList[Subnet]() lazy val proxySubnets = new mutable.SortedList[Subnet]()
var hostnames: mutable.SortedList[String] = if (defaultBypass) proxyHostnames else bypassHostnames var hostnames: mutable.SortedList[String] = if (defaultBypass) proxyHostnames else bypassHostnames
var subnets: mutable.SortedList[Subnet] = if (defaultBypass) proxySubnets else bypassSubnets var subnets: mutable.SortedList[Subnet] = if (defaultBypass) proxySubnets else bypassSubnets
var in_urls = false
for (line <- value.getLines()) (line.indexOf('#') match { for (line <- value.getLines()) (line.indexOf('#') match {
case -1 => line case -1 => if (!in_urls) line else ""
case index => line.substring(0, index) // trim comments case index => {
line.indexOf("URLS_BEGIN") match {
case -1 =>
case index => in_urls = true
}
line.indexOf("URLS_END") match {
case -1 =>
case index => in_urls = false
}
"" // ignore any comment lines
}
}).trim match { }).trim match {
case "[outbound_block_list]" => case "[outbound_block_list]" =>
hostnames = null hostnames = null
...@@ -76,7 +95,10 @@ class Acl { ...@@ -76,7 +95,10 @@ class Acl {
case "[reject_all]" | "[bypass_all]" => bypass = true case "[reject_all]" | "[bypass_all]" => bypass = true
case "[accept_all]" | "[proxy_all]" => bypass = false case "[accept_all]" | "[proxy_all]" => bypass = false
case input if subnets != null && input.nonEmpty => try subnets += Subnet.fromString(input) catch { case input if subnets != null && input.nonEmpty => try subnets += Subnet.fromString(input) catch {
case _: IllegalArgumentException => hostnames += input case _: IllegalArgumentException => if (input.startsWith("http://") || input.startsWith("https://")) {
urls += input
}
hostnames += input
} }
case _ => case _ =>
} }
...@@ -85,9 +107,24 @@ class Acl { ...@@ -85,9 +107,24 @@ class Acl {
} }
final def fromId(id: String): Acl = fromSource(Source.fromFile(Acl.getFile(id))) final def fromId(id: String): Acl = fromSource(Source.fromFile(Acl.getFile(id)))
override def toString: String = { def getAclString(network: Boolean): String = {
val result = new StringBuilder() val result = new StringBuilder()
if (urls.nonEmpty) {
result.append("#URLS_BEGIN\n")
result.append(urls.mkString("\n"))
if (network) {
try {
urls.foreach((url: String) => result.append(Source.fromURL(url).mkString))
} catch {
case e: IOException => // ignore
}
}
result.append("#URLS_END\n")
}
if (result.isEmpty) {
result.append(if (bypass) "[bypass_all]\n" else "[proxy_all]\n") result.append(if (bypass) "[bypass_all]\n" else "[proxy_all]\n")
}
val (bypassList, proxyList) = val (bypassList, proxyList) =
if (bypass) (bypassHostnames.toStream, subnets.toStream.map(_.toString) #::: proxyHostnames.toStream) if (bypass) (bypassHostnames.toStream, subnets.toStream.map(_.toString) #::: proxyHostnames.toStream)
else (subnets.toStream.map(_.toString) #::: bypassHostnames.toStream, proxyHostnames.toStream) else (subnets.toStream.map(_.toString) #::: bypassHostnames.toStream, proxyHostnames.toStream)
...@@ -104,6 +141,10 @@ class Acl { ...@@ -104,6 +141,10 @@ class Acl {
result.toString result.toString
} }
override def toString: String = {
getAclString(false)
}
def isValidCustomRules: Boolean = bypass && bypassHostnames.isEmpty def isValidCustomRules: Boolean = bypass && bypassHostnames.isEmpty
// Don't change: dummy fields for OrmLite interaction // Don't change: dummy fields for OrmLite interaction
...@@ -134,9 +175,9 @@ object Acl { ...@@ -134,9 +175,9 @@ object Acl {
try acl.fromId(CUSTOM_RULES) catch { try acl.fromId(CUSTOM_RULES) catch {
case _: FileNotFoundException => case _: FileNotFoundException =>
} }
acl.bypass = true
acl.bypassHostnames.clear() // everything is bypassed
acl acl
} }
def save(id: String, acl: Acl): Unit = IOUtils.writeString(getFile(id), acl.toString) def save(id: String, acl: Acl, network: Boolean = false): Unit = {
IOUtils.writeString(getFile(id), acl.getAclString(network))
}
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment