Compare commits

...
13 Commits
Author SHA1 Message Date
Max Lv 4490d95c7f Bump version 2017-08-28 22:11:38 -07:00
Max Lv 390cb522d5 Fix #1351 2017-08-22 03:10:27 -07:00
Max Lv b14fdcaed9 Fix the missing selected rules 2017-08-20 18:14:07 +08:00
Max Lv b6adeda924 Add local DNS to CHINALIST routing. #1348 2017-08-20 17:41:30 +08:00
Max Lv 253286c482 Bump version 2017-08-15 12:02:03 +08:00
Max Lv f25406b86c Protect table creation with transaction 2017-08-10 08:39:49 +08:00
Max Lv 2c8718163a Add new lines for remote ACL 2017-08-08 07:45:56 +08:00
Max Lv 450bde5305 Bump version 2017-08-07 09:10:45 +08:00
Max Lv d51bf46413 Refine Custom Rules 2017-08-03 22:47:55 +08:00
Max Lv a69183cc0f Move all network rules to new block 2017-08-03 22:15:49 +08:00
Max Lv 62a0bb59c7 Refine URL based ACL 2017-08-03 21:35:25 +08:00
Max Lv 4eb77c8854 Bump version 2017-08-03 20:30:20 +08:00
Max Lv 839765b724 Fix #1325 2017-08-03 20:24:22 +08:00
6 changed files with 107 additions and 77 deletions
+2 -2
View File
@@ -4,8 +4,8 @@ enablePlugins(AndroidGms)
android.useSupportVectors
name := "shadowsocks"
version := "4.2.0"
versionCode := Some(190)
version := "4.2.4"
versionCode := Some(194)
proguardOptions ++=
"-dontwarn com.google.android.gms.internal.**" ::
@@ -374,7 +374,7 @@ trait BaseService extends Service {
.put("IPNetworkFile", "china_ip_list.txt")
.put("DomainFile", "gfwlist.txt")
case Acl.CHINALIST => config
.put("PrimaryDNS", new JSONArray().put(makeDns("Primary", "119.29.29.29")))
.put("PrimaryDNS", localDns)
.put("AlternativeDNS", remoteDns)
case _ => config
.put("PrimaryDNS", remoteDns)
@@ -19,18 +19,19 @@ import scala.io.Source
class Acl {
@DatabaseField(generatedId = true)
var id: Int = _
val bypassHostnames = new mutable.SortedList[String]()
val proxyHostnames = new mutable.SortedList[String]()
val hostnames = new mutable.SortedList[String]()
val subnets = new mutable.SortedList[Subnet]()
val urls = new mutable.SortedList[String]()
@DatabaseField
var bypass: Boolean = _
def isUrl(url: String): Boolean = url.startsWith("http://") || url.startsWith("https://")
def fromAcl(other: Acl): Acl = {
bypassHostnames.clear()
bypassHostnames ++= other.bypassHostnames
proxyHostnames.clear()
proxyHostnames ++= other.proxyHostnames
hostnames.clear()
hostnames ++= other.hostnames
subnets.clear()
subnets ++= other.subnets
urls.clear()
@@ -38,42 +39,41 @@ class Acl {
bypass = other.bypass
this
}
def fromSource(value: Source, defaultBypass: Boolean = true): Acl = {
bypassHostnames.clear()
proxyHostnames.clear()
this.subnets.clear()
this.urls.clear()
subnets.clear()
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 => if (!in_urls) line else ""
case index =>
if (line.contains("URLS_BEGIN")) in_urls = true
if (line.contains("URLS_END")) in_urls = false
line.substring(0, index) // trim comments
for (line <- value.getLines()) (line.trim.indexOf('#') match {
case 0 => {
line.indexOf("NETWORK_ACL_BEGIN") match {
case -1 =>
case index => in_urls = true
}
line.indexOf("NETWORK_ACL_END") match {
case -1 =>
case index => in_urls = false
}
"" // ignore any comment lines
}
case index => if (!in_urls) line else ""
}).trim match {
// Ignore all section controls
case "[outbound_block_list]" =>
hostnames = null
subnets = null
case "[black_list]" | "[bypass_list]" =>
hostnames = bypassHostnames
subnets = bypassSubnets
case "[white_list]" | "[proxy_list]" =>
hostnames = proxyHostnames
subnets = proxySubnets
case "[reject_all]" | "[bypass_all]" => bypass = true
case "[accept_all]" | "[proxy_all]" => bypass = false
case "[reject_all]" | "[bypass_all]" =>
case "[accept_all]" | "[proxy_all]" =>
case input if subnets != null && input.nonEmpty => try subnets += Subnet.fromString(input) catch {
case _: IllegalArgumentException => if (input.startsWith("http://") || input.startsWith("https://"))
urls += input else hostnames += input
case _: IllegalArgumentException => if (isUrl(input)) {
urls += input
} else {
hostnames += input
}
}
case _ =>
}
this.subnets ++= (if (bypass) proxySubnets else bypassSubnets)
this
}
final def fromId(id: String): Acl = fromSource(Source.fromFile(Acl.getFile(id)))
@@ -81,40 +81,35 @@ class Acl {
def getAclString(network: Boolean): String = {
val result = new StringBuilder()
if (urls.nonEmpty) {
result.append("#URLS_BEGIN\n")
result.append(urls.mkString("\n"))
if (network) {
result.append("\n#NETWORK_ACL_BEGIN\n")
try {
urls.foreach((url: String) => result.append(Source.fromURL(url).mkString))
} catch {
case _: IOException => // ignore
case e: IOException => // ignore
}
result.append("\n#NETWORK_ACL_END\n")
}
result.append("#URLS_END\n")
result.append("\n")
}
if (result.isEmpty) {
result.append(if (bypass) "[bypass_all]\n" else "[proxy_all]\n")
result.append("[bypass_all]\n")
}
val (bypassList, proxyList) =
if (bypass) (bypassHostnames.toStream, subnets.toStream.map(_.toString) #::: proxyHostnames.toStream)
else (subnets.toStream.map(_.toString) #::: bypassHostnames.toStream, proxyHostnames.toStream)
if (bypassList.nonEmpty) {
result.append("[bypass_list]\n")
result.append(bypassList.mkString("\n"))
result.append('\n')
}
if (proxyList.nonEmpty) {
val list = subnets.toStream.map(_.toString) #::: hostnames.toStream
if (list.nonEmpty) {
result.append("[proxy_list]\n")
result.append(proxyList.mkString("\n"))
result.append(list.mkString("\n"))
result.append('\n')
}
result.toString
}
override def toString: String = getAclString(false)
override def toString: String = {
getAclString(false)
}
def isValidCustomRules: Boolean = bypass && bypassHostnames.isEmpty
def isValidCustomRules: Boolean = !hostnames.isEmpty
// Don't change: dummy fields for OrmLite interaction
@@ -125,31 +125,44 @@ class CustomRulesFragment extends ToolbarFragment with Toolbar.OnMenuItemClickLi
})
}
def getItemCount: Int = acl.subnets.size + acl.proxyHostnames.size
def getItemCount: Int = acl.subnets.size + acl.hostnames.size + acl.urls.size
def onBindViewHolder(vh: AclRuleViewHolder, i: Int): Unit = {
val j = i - acl.subnets.size
if (j < 0) vh.bind(acl.subnets(i)) else vh.bind(acl.proxyHostnames(j))
val j = i - acl.urls.size
val k = i - acl.subnets.size - acl.urls.size
if (j < 0)
vh.bind(acl.urls(i))
else if (k < 0)
vh.bind(acl.subnets(j))
else
vh.bind(acl.hostnames(k))
}
def onCreateViewHolder(vg: ViewGroup, i: Int) = new AclRuleViewHolder(LayoutInflater.from(vg.getContext)
.inflate(android.R.layout.simple_list_item_1, vg, false))
def addSubnet(subnet: Subnet): Int = if (acl.subnets.add(subnet)) {
val index = acl.subnets.indexOf(subnet)
def addUrl(url: String): Int = if (acl.urls.add(url)) {
val index = acl.urls.indexOf(url)
notifyItemInserted(index)
apply()
index
} else -1
def addHostname(hostname: String): Int = if (acl.proxyHostnames.add(hostname)) {
val index = acl.proxyHostnames.indexOf(hostname) + acl.subnets.size
def addSubnet(subnet: Subnet): Int = if (acl.subnets.add(subnet)) {
val index = acl.subnets.indexOf(subnet) + acl.urls.size
notifyItemInserted(index)
apply()
index
} else -1
def addHostname(hostname: String): Int = if (acl.hostnames.add(hostname)) {
val index = acl.hostnames.indexOf(hostname) + acl.urls.size + acl.subnets.size
notifyItemInserted(index)
apply()
index
} else -1
def addToProxy(input: String): Int = {
val acl = new Acl().fromSource(Source.fromString(input), defaultBypass = true)
val acl = new Acl().fromSource(Source.fromString(input))
var result = -1
for (hostname <- acl.proxyHostnames) result = addHostname(hostname)
if (acl.bypass) for (subnet <- acl.subnets) result = addSubnet(subnet)
for (url <- acl.urls) result = addUrl(url)
for (hostname <- acl.hostnames) result = addHostname(hostname)
for (subnet <- acl.subnets) result = addSubnet(subnet)
result
}
def addFromTemplate(template: Int, text: CharSequence): Int = template match {
@@ -164,26 +177,36 @@ class CustomRulesFragment extends ToolbarFragment with Toolbar.OnMenuItemClickLi
}
def remove(i: Int) {
val j = i - acl.subnets.size
val j = i - acl.urls.size
val k = i - acl.urls.size - acl.subnets.size
if (j < 0) {
undoManager.remove((i, acl.subnets(i)))
acl.subnets.remove(i)
undoManager.remove((i, acl.urls(i)))
acl.urls.remove(i)
} else if (k < 0) {
undoManager.remove((i, acl.subnets(j)))
acl.subnets.remove(j)
} else {
undoManager.remove((j, acl.proxyHostnames(j)))
acl.proxyHostnames.remove(j)
undoManager.remove((i, acl.hostnames(k)))
acl.hostnames.remove(k)
}
notifyItemRemoved(i)
apply()
}
def remove(item: AnyRef): Unit = item match {
case subnet: Subnet =>
notifyItemRemoved(acl.subnets.indexOf(subnet))
notifyItemRemoved(acl.subnets.indexOf(subnet) + acl.urls.size)
acl.subnets.remove(subnet)
apply()
case hostname: String =>
notifyItemRemoved(acl.proxyHostnames.indexOf(hostname))
acl.proxyHostnames.remove(hostname)
case hostname: String => if (acl.isUrl(hostname)) {
notifyItemRemoved(acl.urls.indexOf(hostname))
acl.urls.remove(hostname)
apply()
} else {
notifyItemRemoved(acl.hostnames.indexOf(hostname)
+ acl.urls.size + acl.subnets.size)
acl.hostnames.remove(hostname)
apply()
}
}
def removeSelected() {
undoManager.remove(selectedItems.map((-1, _)).toSeq: _*)
@@ -192,20 +215,28 @@ class CustomRulesFragment extends ToolbarFragment with Toolbar.OnMenuItemClickLi
onSelectedItemsUpdated()
}
def undo(actions: Iterator[(Int, AnyRef)]): Unit = for ((_, item) <- actions) item match {
case hostname: String => if (acl.proxyHostnames.insert(hostname)) {
notifyItemInserted(acl.proxyHostnames.indexOf(hostname) + acl.subnets.size)
apply()
case hostname: String => if (acl.isUrl(hostname)) {
if (acl.urls.insert(hostname)) {
notifyItemInserted(acl.urls.indexOf(hostname))
apply()
}
} else {
if (acl.hostnames.insert(hostname)) {
notifyItemInserted(acl.hostnames.indexOf(hostname) + acl.urls.size + acl.subnets.size)
apply()
}
}
case subnet: Subnet => if (acl.subnets.insert(subnet)) {
notifyItemInserted(acl.subnets.indexOf(subnet))
notifyItemInserted(acl.subnets.indexOf(subnet) + acl.urls.size)
apply()
}
}
def selectAll() {
selectedItems.clear()
selectedItems ++= acl.urls
selectedItems ++= acl.subnets
selectedItems ++= acl.proxyHostnames
selectedItems ++= acl.hostnames
onSelectedItemsUpdated()
notifyDataSetChanged()
}
@@ -214,7 +245,7 @@ class CustomRulesFragment extends ToolbarFragment with Toolbar.OnMenuItemClickLi
val j = i - acl.subnets.size
try {
(if (j < 0) acl.subnets(i).address.getHostAddress.substring(0, 1) else {
val hostname = acl.proxyHostnames(i)
val hostname = acl.hostnames(i)
PATTERN_DOMAIN.findFirstMatchIn(hostname) match {
case Some(m) => m.matched.replaceAll("\\\\.", ".") // don't convert IDN yet
case None => hostname
@@ -310,7 +341,7 @@ class CustomRulesFragment extends ToolbarFragment with Toolbar.OnMenuItemClickLi
case R.id.action_import_gfwlist =>
val acl = new Acl().fromId(Acl.GFWLIST)
acl.subnets.foreach(adapter.addSubnet)
acl.proxyHostnames.foreach(adapter.addHostname)
acl.hostnames.foreach(adapter.addHostname)
true
case _ => false
}
@@ -141,6 +141,9 @@ class DBHelper(val context: Context)
}
if (oldVersion < 23) {
profileDao.executeRawNoArgs("BEGIN TRANSACTION;")
TableUtils.createTable(connectionSource, classOf[KeyValuePair])
profileDao.executeRawNoArgs("COMMIT;")
import KeyValuePair._
val old = PreferenceManager.getDefaultSharedPreferences(app)
kvPairDao.createOrUpdate(new KeyValuePair(Key.id, TYPE_INT,
+1
View File
@@ -0,0 +1 @@
sbt.version=0.13.16