diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/ProxyActivity.java b/app/src/main/java/org/openobservatory/ooniprobe/activity/ProxyActivity.java
index 46ced49d6..f6c562043 100644
--- a/app/src/main/java/org/openobservatory/ooniprobe/activity/ProxyActivity.java
+++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/ProxyActivity.java
@@ -48,9 +48,15 @@ public class ProxyActivity extends AbstractActivity {
*
* 1. an empty string means no proxy;
*
- * 2. "psiphon://" means that we wanna use psiphon;
+ * 2. "psiphon:///" means that we want to use psiphon;
*
- * 3. "socks5://1.2.3.4:5678" or "socks5://[::1]:5678" or "socks5://d.com:5678"
+ * 3. "tor:///" means we want to use tor without any proxy (which is possible
+ * because oonimkall embeds `libtor.a` as a dependency);
+ *
+ * 4. "torsf:///" is like "tor:///" but additionally uses the snowflake
+ * library we also bundle inside of oonimkall;
+ *
+ * 5. "socks5://1.2.3.4:5678" or "socks5://[::1]:5678" or "socks5://d.com:5678"
* means that we wanna use the given socks5 proxy.
*
* Future improvements
@@ -67,8 +73,6 @@ public class ProxyActivity extends AbstractActivity {
* This implies we can trivially support a vanilla socks5 proxy with username and
* password by just replacing `psiphon+socks5` with `socks5`.
*
- * We also want to support vanilla tor, using `tor://`.
- *
* We also want to support vanilla tor with socks5, which is trivially doable
* using as a scheme the `tor+socks5` scheme.
*
@@ -93,15 +97,20 @@ public class ProxyActivity extends AbstractActivity {
* The design and implementation of this class owes to the code contributed
* by and the suggestion from friendly anonymous users. Thank you!
*/
+
+ // logger is the injected AppLogger instance.
@Inject
AppLogger logger;
+
// TAG is the tag used for logging.
private final static String TAG = "ProxyActivity";
+ // preferenceManager is the injected PreferenceManager instance.
@Inject
PreferenceManager preferenceManager;
- // The following radio group describes the top level choice
- // in terms of proxying: no proxy, psiphon, or custom.
+
+ // The following radio group describes the top level choice in terms of
+ // proxying: no proxy, psiphon, tor, torsf, or custom.
// proxyRadioGroup is the top-level radio group.
private RadioGroup proxyRadioGroup;
@@ -112,12 +121,21 @@ public class ProxyActivity extends AbstractActivity {
// proxyPsiphonRB is the radio button selecting the "psiphon" proxy.
private RadioButton proxyPsiphonRB;
+ // proxyTorRB is the radio button selecting the "tor" proxy.
+ private RadioButton proxyTorRB;
+
+ // proxyTorSfRB is the radio button selecting the "torsf" proxy.
+ private RadioButton proxyTorSfRB;
+
// proxyCustomRB is the radio button for the "custom" proxy.
private RadioButton proxyCustomRB;
// The following radio group allows users to choose which specific
// custom proxy they would like to use. When writing this documentation,
// only socks5 is available but we will add more options.
+ //
+ // TODO(bassosimone): we need to implement support for HTTP proxies
+ // once https://github.com/ooni/probe-cli/pull/1162 lands.
// customProxyRadioGroup allows you to choose among the different
// kinds of custom proxies that are available.
@@ -153,6 +171,8 @@ public void onCreate(Bundle savedInstanceState) {
proxyRadioGroup = findViewById(R.id.proxyRadioGroup);
proxyNoneRB = findViewById(R.id.proxyNone);
proxyPsiphonRB = findViewById(R.id.proxyPsiphon);
+ proxyTorRB = findViewById(R.id.proxyTor);
+ proxyTorSfRB = findViewById(R.id.proxyTorSf);
proxyCustomRB = findViewById(R.id.proxyCustom);
customProxyRadioGroup = findViewById(R.id.customProxyRadioGroup);
customProxySOCKS5 = findViewById(R.id.customProxySOCKS5);
@@ -196,6 +216,10 @@ private void configureInitialViewWithSettings(ProxySettings settings) {
proxyNoneRB.setChecked(true);
} else if (settings.protocol == ProxyProtocol.PSIPHON) {
proxyPsiphonRB.setChecked(true);
+ } else if (settings.protocol == ProxyProtocol.TOR) {
+ proxyTorRB.setChecked(true);
+ } else if (settings.protocol == ProxyProtocol.TORSF) {
+ proxyTorSfRB.setChecked(true);
} else if (settings.protocol == ProxyProtocol.SOCKS5) {
proxyCustomRB.setChecked(true);
} else {
@@ -226,6 +250,10 @@ private void configureInitialViewWithSettings(ProxySettings settings) {
customProxySetEnabled(false);
} else if (checkedId == R.id.proxyPsiphon) {
customProxySetEnabled(false);
+ } else if (checkedId == R.id.proxyTor) {
+ customProxySetEnabled(false);
+ } else if (checkedId == R.id.proxyTorSf) {
+ customProxySetEnabled(false);
} else if (checkedId == R.id.proxyCustom) {
customProxySetEnabled(true);
customProxyRadioGroup.clearCheck();
@@ -364,6 +392,24 @@ public void onBackPressed() {
return;
}
+ // If the tor proxy is checked then write back the right
+ // proxy configuration for tor and move on.
+ if (proxyTorRB.isChecked()) {
+ settings.protocol = ProxyProtocol.TOR;
+ saveSettings();
+ super.onBackPressed();
+ return;
+ }
+
+ // If the torsf proxy is checked then write back the right
+ // proxy configuration for torsf and move on.
+ if (proxyTorSfRB.isChecked()) {
+ settings.protocol = ProxyProtocol.TORSF;
+ saveSettings();
+ super.onBackPressed();
+ return;
+ }
+
// validate the hostname for the custom proxy.
if (!isValidHostnameOrIP(hostname)) {
customProxyHostname.setError("not a valid hostname or IP");
diff --git a/app/src/main/java/org/openobservatory/ooniprobe/common/ProxyProtocol.java b/app/src/main/java/org/openobservatory/ooniprobe/common/ProxyProtocol.java
index ed5742a2e..ee7e8614e 100644
--- a/app/src/main/java/org/openobservatory/ooniprobe/common/ProxyProtocol.java
+++ b/app/src/main/java/org/openobservatory/ooniprobe/common/ProxyProtocol.java
@@ -4,6 +4,8 @@
public enum ProxyProtocol {
NONE("none"),
PSIPHON("psiphon"),
+ TOR("tor"),
+ TORSF("torsf"),
SOCKS5("socks5");
private String protocol;
diff --git a/app/src/main/java/org/openobservatory/ooniprobe/common/ProxySettings.java b/app/src/main/java/org/openobservatory/ooniprobe/common/ProxySettings.java
index e19867f94..78404b833 100644
--- a/app/src/main/java/org/openobservatory/ooniprobe/common/ProxySettings.java
+++ b/app/src/main/java/org/openobservatory/ooniprobe/common/ProxySettings.java
@@ -13,8 +13,7 @@
* documentation of proxy activity for the design rationale.
*/
public class ProxySettings {
-
- /** scheme is the proxy scheme (e.g., "psiphon", "socks5"). */
+ /** scheme is the proxy scheme (e.g., "psiphon", "tor", "torsf", "socks5"). */
public ProxyProtocol protocol = ProxyProtocol.NONE;
/** hostname is the hostname for custom proxies. */
@@ -33,10 +32,14 @@ public static ProxySettings newProxySettings(PreferenceManager pm) throws Invali
settings.protocol = ProxyProtocol.NONE;
} else if (protocol.equals(ProxyProtocol.PSIPHON.getProtocol())) {
settings.protocol = ProxyProtocol.PSIPHON;
+ } else if (protocol.equals(ProxyProtocol.TOR.getProtocol())) {
+ settings.protocol = ProxyProtocol.TOR;
+ } else if (protocol.equals(ProxyProtocol.TORSF.getProtocol())) {
+ settings.protocol = ProxyProtocol.TORSF;
} else if (protocol.equals(ProxyProtocol.SOCKS5.getProtocol())) {
settings.protocol = ProxyProtocol.SOCKS5;
} else {
- // This is where we will extend the code to add support for
+ // This exception indicates that we need to extend the code to support
// more proxies, e.g., HTTP proxies.
throw new InvalidProxyURL("unhandled URL scheme");
}
@@ -72,10 +75,18 @@ private boolean isIPv6(String hostname) {
/** getProxyString returns to you the proxy string you should pass to oonimkall. */
public String getProxyString() throws URISyntaxException {
- if (protocol == ProxyProtocol.NONE)
+ if (protocol == ProxyProtocol.NONE) {
return "";
- if (protocol == ProxyProtocol.PSIPHON)
- return "psiphon://";
+ }
+ if (protocol == ProxyProtocol.PSIPHON) {
+ return "psiphon:///";
+ }
+ if (protocol == ProxyProtocol.TOR) {
+ return "tor:///";
+ }
+ if (protocol == ProxyProtocol.TORSF) {
+ return "torsf:///";
+ }
if (protocol == ProxyProtocol.SOCKS5) {
// Alright, we now need to construct a new SOCKS5 URL. We are going to defer
// doing that to the Java standard library (er, the Android stdlib).
diff --git a/app/src/main/res/layout/activity_proxy.xml b/app/src/main/res/layout/activity_proxy.xml
index d3c565ed4..6b7da69b1 100644
--- a/app/src/main/res/layout/activity_proxy.xml
+++ b/app/src/main/res/layout/activity_proxy.xml
@@ -38,6 +38,20 @@
android:textColor="@color/color_black"
android:text="@string/Settings_Proxy_Psiphon" />
+
+
+
+
-
\ No newline at end of file
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index db87e2f35..a9a48c990 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -392,6 +392,8 @@
Proxy
None
Psiphon
+ Tor (vanilla)
+ Tor with Snowflake
Custom Proxy
Custom Proxy URL
Custom proxy protocol