From a84070c2d3b445a38c8c789c4c8ee0933fac22f6 Mon Sep 17 00:00:00 2001 From: Max Lv Date: Wed, 14 Jun 2017 15:27:35 +0800 Subject: [PATCH] Allow adding URLs to custom rules. #1276 --- mobile/src/main/res/values-ja/strings.xml | 2 +- mobile/src/main/res/values-ko/strings.xml | 2 +- mobile/src/main/res/values-ru/strings.xml | 2 +- mobile/src/main/res/values-zh-rCN/strings.xml | 2 +- mobile/src/main/res/values-zh-rTW/strings.xml | 2 +- mobile/src/main/res/values/strings.xml | 2 +- .../com/github/shadowsocks/BaseService.scala | 2 +- .../com/github/shadowsocks/acl/Acl.scala | 61 ++++++++++++++++--- 8 files changed, 58 insertions(+), 17 deletions(-) diff --git a/mobile/src/main/res/values-ja/strings.xml b/mobile/src/main/res/values-ja/strings.xml index 366833f1..fa52639a 100644 --- a/mobile/src/main/res/values-ja/strings.xml +++ b/mobile/src/main/res/values-ja/strings.xml @@ -123,7 +123,7 @@ "LAN 及び中国本土のアドレスを迂回する" "中国本土のアドレス以外を迂回する" "選択したアプリにプロキシを設定する" -"サブネット/ホスト名 PCRE パターン" +"URL/サブネット/ホスト名 PCRE パターン" "ドメイン及び全てのサブドメイン" diff --git a/mobile/src/main/res/values-ko/strings.xml b/mobile/src/main/res/values-ko/strings.xml index c7eab897..013fb557 100644 --- a/mobile/src/main/res/values-ko/strings.xml +++ b/mobile/src/main/res/values-ko/strings.xml @@ -45,4 +45,4 @@ "지원하지 않는 버전의 커널입니다: %s < 3.7.1" "DNS 포워딩" "모든 DNS 요청을 외부로 포워딩 합니다" - \ No newline at end of file + diff --git a/mobile/src/main/res/values-ru/strings.xml b/mobile/src/main/res/values-ru/strings.xml index a36dbe08..37db95cf 100644 --- a/mobile/src/main/res/values-ru/strings.xml +++ b/mobile/src/main/res/values-ru/strings.xml @@ -131,7 +131,7 @@ "Все, кроме LAN и Китая" "Список Китай" "Установить прокси для выбранных приложений" -"Подсеть/Регулярное выражение (PCRE) имени хоста" +"URL/Подсеть/Регулярное выражение (PCRE) имени хоста" "Доменное имя и все его поддомены" diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml index ed29ac07..390e2e3e 100644 --- a/mobile/src/main/res/values-zh-rCN/strings.xml +++ b/mobile/src/main/res/values-zh-rCN/strings.xml @@ -124,7 +124,7 @@ "绕过局域网及中国大陆地址" "仅代理中国大陆地址" "为应用程序分别设置代理" -"子网/域名 PCRE 正则表达式" +"URL/子网/域名 PCRE 正则表达式" "域名及其子域名" diff --git a/mobile/src/main/res/values-zh-rTW/strings.xml b/mobile/src/main/res/values-zh-rTW/strings.xml index 431d1830..3b05c65d 100644 --- a/mobile/src/main/res/values-zh-rTW/strings.xml +++ b/mobile/src/main/res/values-zh-rTW/strings.xml @@ -122,7 +122,7 @@ "略過區域網路及中國大陸" "China List" "為已選擇的應用程式設定 Proxy" -"子網路/主機名稱 PCRE 模式" +"URL/子網路/主機名稱 PCRE 模式" "網域及其所有子網域" diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index 60faa91f..d97b17f9 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -130,7 +130,7 @@ Custom rules Selection… Add rule(s)… - Subnet/Hostname PCRE pattern + URL, Subnet or Hostname PCRE pattern Domain name and all its subdomain names Edit rule diff --git a/mobile/src/main/scala/com/github/shadowsocks/BaseService.scala b/mobile/src/main/scala/com/github/shadowsocks/BaseService.scala index b9a8ac02..3727d405 100644 --- a/mobile/src/main/scala/com/github/shadowsocks/BaseService.scala +++ b/mobile/src/main/scala/com/github/shadowsocks/BaseService.scala @@ -154,7 +154,7 @@ trait BaseService extends Service { } 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 pluginPath = PluginManager.init(plugin) diff --git a/mobile/src/main/scala/com/github/shadowsocks/acl/Acl.scala b/mobile/src/main/scala/com/github/shadowsocks/acl/Acl.scala index 676f09e4..dc953f6c 100644 --- a/mobile/src/main/scala/com/github/shadowsocks/acl/Acl.scala +++ b/mobile/src/main/scala/com/github/shadowsocks/acl/Acl.scala @@ -1,6 +1,6 @@ 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.utils.IOUtils @@ -22,6 +22,7 @@ class Acl { val bypassHostnames = new mutable.SortedList[String]() val proxyHostnames = new mutable.SortedList[String]() val subnets = new mutable.SortedList[Subnet]() + val urls = new mutable.SortedList[String]() @DatabaseField var bypass: Boolean = _ @@ -40,6 +41,10 @@ class Acl { subnets.clear() subnets ++= value.split("\n").map(Subnet.fromString) } + def setUrlRules(value: String) { + urls.clear() + urls ++= value.split("\n") + } def fromAcl(other: Acl): Acl = { bypassHostnames.clear() @@ -48,21 +53,35 @@ class Acl { proxyHostnames ++= other.proxyHostnames subnets.clear() subnets ++= other.subnets + urls.clear() + urls ++= other.urls bypass = other.bypass this } - def fromSource(value: Source, defaultBypass: Boolean = false): Acl = { + def fromSource(value: Source, defaultBypass: Boolean = true): Acl = { bypassHostnames.clear() proxyHostnames.clear() this.subnets.clear() + this.urls.clear() bypass = defaultBypass lazy val bypassSubnets = new mutable.SortedList[Subnet]() lazy val proxySubnets = new mutable.SortedList[Subnet]() var hostnames: mutable.SortedList[String] = if (defaultBypass) proxyHostnames else bypassHostnames var subnets: mutable.SortedList[Subnet] = if (defaultBypass) proxySubnets else bypassSubnets + var in_urls = false for (line <- value.getLines()) (line.indexOf('#') match { - case -1 => line - case index => line.substring(0, index) // trim comments + case -1 => if (!in_urls) line else "" + 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 { case "[outbound_block_list]" => hostnames = null @@ -76,7 +95,10 @@ class Acl { case "[reject_all]" | "[bypass_all]" => bypass = true case "[accept_all]" | "[proxy_all]" => bypass = false 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 _ => } @@ -85,9 +107,24 @@ class Acl { } 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() - result.append(if (bypass) "[bypass_all]\n" else "[proxy_all]\n") + 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") + } val (bypassList, proxyList) = if (bypass) (bypassHostnames.toStream, subnets.toStream.map(_.toString) #::: proxyHostnames.toStream) else (subnets.toStream.map(_.toString) #::: bypassHostnames.toStream, proxyHostnames.toStream) @@ -104,6 +141,10 @@ class Acl { result.toString } + override def toString: String = { + getAclString(false) + } + def isValidCustomRules: Boolean = bypass && bypassHostnames.isEmpty // Don't change: dummy fields for OrmLite interaction @@ -134,9 +175,9 @@ object Acl { try acl.fromId(CUSTOM_RULES) catch { case _: FileNotFoundException => } - acl.bypass = true - acl.bypassHostnames.clear() // everything is bypassed 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)) + } }