Pier Angelo Vendrame pushed to branch tor-browser-115.6.0esr-13.5-1 at The Tor Project / Applications / Tor Browser
Commits:
- 
1edf6cd0
by Pier Angelo Vendrame at 2023-12-20T17:50:17+00:00
- 
e9cc4840
by Dan Ballard at 2023-12-20T17:50:17+00:00
6 changed files:
- mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
- mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorIntegrationAndroid.java
- + mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorSettings.java
- + mobile/android/geckoview/src/main/java/org/mozilla/geckoview/androidlegacysettings/Prefs.java
- + mobile/android/geckoview/src/main/java/org/mozilla/geckoview/androidlegacysettings/TorLegacyAndroidSettings.java
- toolkit/modules/TorAndroidIntegration.sys.mjs
Changes:
| ... | ... | @@ -1008,6 +1008,14 @@ public final class GeckoRuntime implements Parcelable { | 
| 1008 | 1008 |      return mPushController;
 | 
| 1009 | 1009 |    }
 | 
| 1010 | 1010 | |
| 1011 | +  /**
 | |
| 1012 | +   * Get the Tor integration controller for this runtime.
 | |
| 1013 | +   */
 | |
| 1014 | +  @UiThread
 | |
| 1015 | +  public @NonNull TorIntegrationAndroid getTorIntegrationController() {
 | |
| 1016 | +    return mTorIntegration;
 | |
| 1017 | +  }
 | |
| 1018 | + | |
| 1011 | 1019 |    /**
 | 
| 1012 | 1020 |     * Appends notes to crash report.
 | 
| 1013 | 1021 |     *
 | 
| ... | ... | @@ -9,6 +9,10 @@ package org.mozilla.geckoview; | 
| 9 | 9 |  import android.content.Context;
 | 
| 10 | 10 |  import android.util.Log;
 | 
| 11 | 11 | |
| 12 | +import androidx.annotation.AnyThread;
 | |
| 13 | +import androidx.annotation.NonNull;
 | |
| 14 | +import androidx.annotation.Nullable;
 | |
| 15 | + | |
| 12 | 16 |  import java.io.BufferedReader;
 | 
| 13 | 17 |  import java.io.File;
 | 
| 14 | 18 |  import java.io.IOException;
 | 
| ... | ... | @@ -22,9 +26,9 @@ import java.nio.file.attribute.PosixFilePermission; | 
| 22 | 26 |  import java.nio.file.attribute.PosixFilePermissions;
 | 
| 23 | 27 |  import java.util.ArrayList;
 | 
| 24 | 28 |  import java.util.HashMap;
 | 
| 29 | +import java.util.HashSet;
 | |
| 25 | 30 |  import java.util.Map;
 | 
| 26 | 31 |  import java.util.Set;
 | 
| 27 | -import java.util.UUID;
 | |
| 28 | 32 | |
| 29 | 33 |  import org.mozilla.gecko.EventDispatcher;
 | 
| 30 | 34 |  import org.mozilla.gecko.GeckoAppShell;
 | 
| ... | ... | @@ -32,13 +36,27 @@ import org.mozilla.gecko.util.BundleEventListener; | 
| 32 | 36 |  import org.mozilla.gecko.util.EventCallback;
 | 
| 33 | 37 |  import org.mozilla.gecko.util.GeckoBundle;
 | 
| 34 | 38 | |
| 35 | -/* package */ class TorIntegrationAndroid implements BundleEventListener {
 | |
| 39 | +import org.mozilla.geckoview.androidlegacysettings.TorLegacyAndroidSettings;
 | |
| 40 | + | |
| 41 | +public class TorIntegrationAndroid implements BundleEventListener {
 | |
| 36 | 42 |      private static final String TAG = "TorIntegrationAndroid";
 | 
| 37 | 43 | |
| 38 | -    private static final String TOR_EVENT_START = "GeckoView:Tor:StartTor";
 | |
| 39 | -    private static final String TOR_EVENT_STOP = "GeckoView:Tor:StopTor";
 | |
| 40 | -    private static final String MEEK_EVENT_START = "GeckoView:Tor:StartMeek";
 | |
| 41 | -    private static final String MEEK_EVENT_STOP = "GeckoView:Tor:StopMeek";
 | |
| 44 | +    // Events we listen to
 | |
| 45 | +    private static final String EVENT_TOR_START = "GeckoView:Tor:StartTor";
 | |
| 46 | +    private static final String EVENT_TOR_STOP = "GeckoView:Tor:StopTor";
 | |
| 47 | +    private static final String EVENT_MEEK_START = "GeckoView:Tor:StartMeek";
 | |
| 48 | +    private static final String EVENT_MEEK_STOP = "GeckoView:Tor:StopMeek";
 | |
| 49 | + | |
| 50 | +    // Events we emit
 | |
| 51 | +    private static final String EVENT_SETTINGS_GET = "GeckoView:Tor:SettingsGet";
 | |
| 52 | +    private static final String EVENT_SETTINGS_SET = "GeckoView:Tor:SettingsSet";
 | |
| 53 | +    private static final String EVENT_SETTINGS_APPLY = "GeckoView:Tor:SettingsApply";
 | |
| 54 | +    private static final String EVENT_SETTINGS_SAVE = "GeckoView:Tor:SettingsSave";
 | |
| 55 | +    private static final String EVENT_BOOTSTRAP_BEGIN = "GeckoView:Tor:BootstrapBegin";
 | |
| 56 | +    private static final String EVENT_BOOTSTRAP_BEGIN_AUTO = "GeckoView:Tor:BootstrapBeginAuto";
 | |
| 57 | +    private static final String EVENT_BOOTSTRAP_CANCEL = "GeckoView:Tor:BootstrapCancel";
 | |
| 58 | +    private static final String EVENT_BOOTSTRAP_GET_STATE = "GeckoView:Tor:BootstrapGetState";
 | |
| 59 | +    private static final String EVENT_SETTINGS_READY = "GeckoView:Tor:SettingsReady";
 | |
| 42 | 60 | |
| 43 | 61 |      private static final String CONTROL_PORT_FILE = "/control-ipc";
 | 
| 44 | 62 |      private static final String SOCKS_FILE = "/socks-ipc";
 | 
| ... | ... | @@ -63,7 +81,9 @@ import org.mozilla.gecko.util.GeckoBundle; | 
| 63 | 81 |      private final HashMap<Integer, MeekTransport> mMeeks = new HashMap<>();
 | 
| 64 | 82 |      private int mMeekCounter;
 | 
| 65 | 83 | |
| 66 | -    public TorIntegrationAndroid(Context context) {
 | |
| 84 | +    private TorSettings mSettings = null;
 | |
| 85 | + | |
| 86 | +    /* package */ TorIntegrationAndroid(Context context) {
 | |
| 67 | 87 |          mLibraryDir = context.getApplicationInfo().nativeLibraryDir;
 | 
| 68 | 88 |          mCacheDir = context.getCacheDir().toPath();
 | 
| 69 | 89 |          mIpcDirectory = mCacheDir + "/tor-private";
 | 
| ... | ... | @@ -71,7 +91,7 @@ import org.mozilla.gecko.util.GeckoBundle; | 
| 71 | 91 |          registerListener();
 | 
| 72 | 92 |      }
 | 
| 73 | 93 | |
| 74 | -    public synchronized void shutdown() {
 | |
| 94 | +    /* package */ synchronized void shutdown() {
 | |
| 75 | 95 |          // FIXME: It seems this never gets called
 | 
| 76 | 96 |          if (mTorProcess != null) {
 | 
| 77 | 97 |              mTorProcess.shutdown();
 | 
| ... | ... | @@ -83,22 +103,36 @@ import org.mozilla.gecko.util.GeckoBundle; | 
| 83 | 103 |          EventDispatcher.getInstance()
 | 
| 84 | 104 |                  .registerUiThreadListener(
 | 
| 85 | 105 |                          this,
 | 
| 86 | -                        TOR_EVENT_START,
 | |
| 87 | -                        MEEK_EVENT_START,
 | |
| 88 | -                        MEEK_EVENT_STOP);
 | |
| 106 | +                        EVENT_TOR_START,
 | |
| 107 | +                        EVENT_MEEK_START,
 | |
| 108 | +                        EVENT_MEEK_STOP,
 | |
| 109 | +                        EVENT_SETTINGS_READY);
 | |
| 89 | 110 |      }
 | 
| 90 | 111 | |
| 91 | 112 |      @Override // BundleEventListener
 | 
| 92 | 113 |      public synchronized void handleMessage(
 | 
| 93 | 114 |              final String event, final GeckoBundle message, final EventCallback callback) {
 | 
| 94 | -        if (TOR_EVENT_START.equals(event)) {
 | |
| 115 | +        if (EVENT_TOR_START.equals(event)) {
 | |
| 95 | 116 |              startDaemon(message, callback);
 | 
| 96 | -        } else if (TOR_EVENT_STOP.equals(event)) {
 | |
| 117 | +        } else if (EVENT_TOR_STOP.equals(event)) {
 | |
| 97 | 118 |              stopDaemon(message, callback);
 | 
| 98 | -        } else if (MEEK_EVENT_START.equals(event)) {
 | |
| 119 | +        } else if (EVENT_MEEK_START.equals(event)) {
 | |
| 99 | 120 |              startMeek(message, callback);
 | 
| 100 | -        } else if (MEEK_EVENT_STOP.equals(event)) {
 | |
| 121 | +        } else if (EVENT_MEEK_STOP.equals(event)) {
 | |
| 101 | 122 |              stopMeek(message, callback);
 | 
| 123 | +        } else if (EVENT_SETTINGS_READY.equals(event)) {
 | |
| 124 | +            loadSettings(message);
 | |
| 125 | +        }
 | |
| 126 | +    }
 | |
| 127 | + | |
| 128 | +    private void loadSettings(GeckoBundle message) {
 | |
| 129 | +        if (TorLegacyAndroidSettings.unmigrated()) {
 | |
| 130 | +            mSettings = TorLegacyAndroidSettings.loadTorSettings();
 | |
| 131 | +            setSettings(mSettings);
 | |
| 132 | +            TorLegacyAndroidSettings.setMigrated();
 | |
| 133 | +        } else {
 | |
| 134 | +            GeckoBundle bundle = message.getBundle("settings");
 | |
| 135 | +            mSettings = new TorSettings(bundle);
 | |
| 102 | 136 |          }
 | 
| 103 | 137 |      }
 | 
| 104 | 138 | |
| ... | ... | @@ -145,9 +179,9 @@ import org.mozilla.gecko.util.GeckoBundle; | 
| 145 | 179 |      }
 | 
| 146 | 180 | |
| 147 | 181 |      class TorProcess extends Thread {
 | 
| 148 | -        private static final String TOR_EVENT_STARTED = "GeckoView:Tor:TorStarted";
 | |
| 149 | -        private static final String TOR_EVENT_START_FAILED = "GeckoView:Tor:TorStartFailed";
 | |
| 150 | -        private static final String TOR_EVENT_EXITED = "GeckoView:Tor:TorExited";
 | |
| 182 | +        private static final String EVENT_TOR_STARTED = "GeckoView:Tor:TorStarted";
 | |
| 183 | +        private static final String EVENT_TOR_START_FAILED = "GeckoView:Tor:TorStartFailed";
 | |
| 184 | +        private static final String EVENT_TOR_EXITED = "GeckoView:Tor:TorExited";
 | |
| 151 | 185 |          private final String mHandle;
 | 
| 152 | 186 |          private Process mProcess = null;
 | 
| 153 | 187 | |
| ... | ... | @@ -202,14 +236,14 @@ import org.mozilla.gecko.util.GeckoBundle; | 
| 202 | 236 |                  final GeckoBundle data = new GeckoBundle(2);
 | 
| 203 | 237 |                  data.putString("handle", mHandle);
 | 
| 204 | 238 |                  data.putString("error", e.getMessage());
 | 
| 205 | -                EventDispatcher.getInstance().dispatch(TOR_EVENT_START_FAILED, data);
 | |
| 239 | +                EventDispatcher.getInstance().dispatch(EVENT_TOR_START_FAILED, data);
 | |
| 206 | 240 |                  return;
 | 
| 207 | 241 |              }
 | 
| 208 | 242 |              Log.i(TAG, "Tor process " + mHandle + " started.");
 | 
| 209 | 243 |              {
 | 
| 210 | 244 |                  final GeckoBundle data = new GeckoBundle(1);
 | 
| 211 | 245 |                  data.putString("handle", mHandle);
 | 
| 212 | -                EventDispatcher.getInstance().dispatch(TOR_EVENT_STARTED, data);
 | |
| 246 | +                EventDispatcher.getInstance().dispatch(EVENT_TOR_STARTED, data);
 | |
| 213 | 247 |              }
 | 
| 214 | 248 |              try {
 | 
| 215 | 249 |                  BufferedReader reader = new BufferedReader(new InputStreamReader(mProcess.getInputStream()));
 | 
| ... | ... | @@ -232,7 +266,7 @@ import org.mozilla.gecko.util.GeckoBundle; | 
| 232 | 266 |              // FIXME: We usually don't reach this when the application is killed!
 | 
| 233 | 267 |              // So, we don't do our cleanup.
 | 
| 234 | 268 |              Log.i(TAG, "Tor process " + mHandle + " has exited.");
 | 
| 235 | -            EventDispatcher.getInstance().dispatch(TOR_EVENT_EXITED, data);
 | |
| 269 | +            EventDispatcher.getInstance().dispatch(EVENT_TOR_EXITED, data);
 | |
| 236 | 270 |          }
 | 
| 237 | 271 | |
| 238 | 272 |          private void cleanIpcDirectory() {
 | 
| ... | ... | @@ -432,4 +466,71 @@ import org.mozilla.gecko.util.GeckoBundle; | 
| 432 | 466 |              }
 | 
| 433 | 467 |          }
 | 
| 434 | 468 |      }
 | 
| 469 | + | |
| 470 | +    public static class BootstrapState {
 | |
| 471 | +        // FIXME: We can do better than this :)
 | |
| 472 | +        public GeckoBundle mBundle;
 | |
| 473 | + | |
| 474 | +        BootstrapState(GeckoBundle bundle) {
 | |
| 475 | +            mBundle = bundle;
 | |
| 476 | +        }
 | |
| 477 | +    }
 | |
| 478 | + | |
| 479 | +    public interface BootstrapStateChangeListener {
 | |
| 480 | +        void onBootstrapStateChange(BootstrapState state);
 | |
| 481 | +    }
 | |
| 482 | + | |
| 483 | +    public @NonNull GeckoResult<GeckoBundle> getSettings() {
 | |
| 484 | +        return EventDispatcher.getInstance().queryBundle(EVENT_SETTINGS_GET);
 | |
| 485 | +    }
 | |
| 486 | + | |
| 487 | +    public @NonNull GeckoResult<Void> setSettings(final TorSettings settings) {
 | |
| 488 | +        return EventDispatcher.getInstance().queryVoid(EVENT_SETTINGS_SET, settings.asGeckoBundle());
 | |
| 489 | +    }
 | |
| 490 | + | |
| 491 | +    public @NonNull GeckoResult<Void> applySettings() {
 | |
| 492 | +        return EventDispatcher.getInstance().queryVoid(EVENT_SETTINGS_APPLY);
 | |
| 493 | +    }
 | |
| 494 | + | |
| 495 | +    public @NonNull GeckoResult<Void> saveSettings() {
 | |
| 496 | +        return EventDispatcher.getInstance().queryVoid(EVENT_SETTINGS_SAVE);
 | |
| 497 | +    }
 | |
| 498 | + | |
| 499 | +    public @NonNull GeckoResult<Void> beginBootstrap() {
 | |
| 500 | +        return EventDispatcher.getInstance().queryVoid(EVENT_BOOTSTRAP_BEGIN);
 | |
| 501 | +    }
 | |
| 502 | + | |
| 503 | +    public @NonNull GeckoResult<Void> beginAutoBootstrap(final String countryCode) {
 | |
| 504 | +        final GeckoBundle bundle = new GeckoBundle(1);
 | |
| 505 | +        bundle.putString("countryCode", countryCode);
 | |
| 506 | +        return EventDispatcher.getInstance().queryVoid(EVENT_BOOTSTRAP_BEGIN_AUTO, bundle);
 | |
| 507 | +    }
 | |
| 508 | + | |
| 509 | +    public @NonNull GeckoResult<Void> beginAutoBootstrap() {
 | |
| 510 | +        return beginAutoBootstrap(null);
 | |
| 511 | +    }
 | |
| 512 | + | |
| 513 | +    public @NonNull GeckoResult<Void> cancelBootstrap() {
 | |
| 514 | +        return EventDispatcher.getInstance().queryVoid(EVENT_BOOTSTRAP_CANCEL);
 | |
| 515 | +    }
 | |
| 516 | + | |
| 517 | +    public @NonNull GeckoResult<BootstrapState> getBootstrapState() {
 | |
| 518 | +        return EventDispatcher.getInstance().queryBundle(EVENT_BOOTSTRAP_GET_STATE).map(new GeckoResult.OnValueMapper<>() {
 | |
| 519 | +            @AnyThread
 | |
| 520 | +            @Nullable
 | |
| 521 | +            public BootstrapState onValue(@Nullable GeckoBundle value) throws Throwable {
 | |
| 522 | +                return new BootstrapState(value);
 | |
| 523 | +            }
 | |
| 524 | +        });
 | |
| 525 | +    }
 | |
| 526 | + | |
| 527 | +    public void registerBootstrapStateChangeListener(BootstrapStateChangeListener listener) {
 | |
| 528 | +        mBootstrapStateListeners.add(listener);
 | |
| 529 | +    }
 | |
| 530 | + | |
| 531 | +    public void unregisterBootstrapStateChangeListener(BootstrapStateChangeListener listener) {
 | |
| 532 | +        mBootstrapStateListeners.remove(listener);
 | |
| 533 | +    }
 | |
| 534 | + | |
| 535 | +    private final HashSet<BootstrapStateChangeListener> mBootstrapStateListeners = new HashSet<>();
 | |
| 435 | 536 |  } | 
| 1 | +package org.mozilla.geckoview;
 | |
| 2 | + | |
| 3 | +import android.util.Log;
 | |
| 4 | + | |
| 5 | +import org.json.JSONArray;
 | |
| 6 | +import org.json.JSONObject;
 | |
| 7 | +import org.mozilla.gecko.util.GeckoBundle;
 | |
| 8 | + | |
| 9 | +import java.io.ByteArrayInputStream;
 | |
| 10 | +import java.io.File;
 | |
| 11 | +import java.io.FileOutputStream;
 | |
| 12 | +import java.io.IOException;
 | |
| 13 | +import java.io.InputStream;
 | |
| 14 | +import java.io.PrintStream;
 | |
| 15 | +import java.io.SequenceInputStream;
 | |
| 16 | +import java.io.UnsupportedEncodingException;
 | |
| 17 | +import java.util.ArrayList;
 | |
| 18 | +import java.util.Arrays;
 | |
| 19 | +import java.util.List;
 | |
| 20 | +import java.util.Locale;
 | |
| 21 | +import java.util.stream.Collectors;
 | |
| 22 | + | |
| 23 | +public class TorSettings {
 | |
| 24 | + | |
| 25 | +    public enum BridgeSource {
 | |
| 26 | +        Invalid(-1),
 | |
| 27 | +        BuiltIn(0),
 | |
| 28 | +        BridgeDB(1),
 | |
| 29 | +        UserProvided(2);
 | |
| 30 | + | |
| 31 | +        private int source;
 | |
| 32 | + | |
| 33 | +        BridgeSource(final int source) {
 | |
| 34 | +            this.source = source;
 | |
| 35 | +        }
 | |
| 36 | + | |
| 37 | +        public static BridgeSource fromInt(int i) {
 | |
| 38 | +            switch (i) {
 | |
| 39 | +                case -1: return Invalid;
 | |
| 40 | +                case 0: return BuiltIn;
 | |
| 41 | +                case 1: return BridgeDB;
 | |
| 42 | +                case 2: return UserProvided;
 | |
| 43 | +            }
 | |
| 44 | +            return Invalid;
 | |
| 45 | +        }
 | |
| 46 | + | |
| 47 | +        public int toInt() {
 | |
| 48 | +            return this.source;
 | |
| 49 | +        }
 | |
| 50 | +    }
 | |
| 51 | + | |
| 52 | +    public enum ProxyType {
 | |
| 53 | +        Invalid(-1),
 | |
| 54 | +        Socks4(0),
 | |
| 55 | +        Socks5(1),
 | |
| 56 | +        HTTPS(2);
 | |
| 57 | + | |
| 58 | +        private int type;
 | |
| 59 | + | |
| 60 | +        ProxyType(final int type) {
 | |
| 61 | +            this.type = type;
 | |
| 62 | +        }
 | |
| 63 | + | |
| 64 | +        public int toInt() {
 | |
| 65 | +            return type;
 | |
| 66 | +        }
 | |
| 67 | + | |
| 68 | +        public static ProxyType fromInt(int i) {
 | |
| 69 | +            switch (i) {
 | |
| 70 | +                case -1: return Invalid;
 | |
| 71 | +                case 0: return Socks4;
 | |
| 72 | +                case 1: return Socks5;
 | |
| 73 | +                case 2: return HTTPS;
 | |
| 74 | +            }
 | |
| 75 | +            return Invalid;
 | |
| 76 | +        }
 | |
| 77 | +    }
 | |
| 78 | + | |
| 79 | +    private boolean loaded = false;
 | |
| 80 | + | |
| 81 | +    public boolean enabled = true;
 | |
| 82 | + | |
| 83 | +    public boolean quickstart = false;
 | |
| 84 | + | |
| 85 | +    // bridges section
 | |
| 86 | +    public boolean bridgesEnabled = false;
 | |
| 87 | +    public BridgeSource bridgesSource = BridgeSource.Invalid;
 | |
| 88 | +    public String bridgesBuiltinType = "";
 | |
| 89 | +    public String[] bridgeBridgeStrings;
 | |
| 90 | + | |
| 91 | +    // proxy section
 | |
| 92 | +    public boolean proxyEnabled = false;
 | |
| 93 | +    public ProxyType proxyType = ProxyType.Invalid;
 | |
| 94 | +    public String proxyAddress = "";
 | |
| 95 | +    public int proxyPort = 0;
 | |
| 96 | +    public String proxyUsername = "";
 | |
| 97 | +    public String proxyPassword = "";
 | |
| 98 | + | |
| 99 | +    // firewall section
 | |
| 100 | +    public boolean firewallEnabled = false;
 | |
| 101 | +    public int[] firewallAllowedPorts;
 | |
| 102 | + | |
| 103 | +    public TorSettings() {
 | |
| 104 | +    }
 | |
| 105 | + | |
| 106 | +    public TorSettings(GeckoBundle bundle) {
 | |
| 107 | +        try {
 | |
| 108 | +            GeckoBundle qs = bundle.getBundle("quickstart");
 | |
| 109 | +            GeckoBundle bridges = bundle.getBundle("bridges");
 | |
| 110 | +            GeckoBundle proxy = bundle.getBundle("proxy");
 | |
| 111 | +            GeckoBundle firewall = bundle.getBundle("firewall");
 | |
| 112 | + | |
| 113 | +            bridgesEnabled = bridges.getBoolean("enabled");
 | |
| 114 | +            bridgesSource = BridgeSource.fromInt(bridges.getInt("source"));
 | |
| 115 | +            bridgesBuiltinType = bridges.getString("builtin_type");
 | |
| 116 | +            bridgeBridgeStrings = bridges.getStringArray("bridge_strings");
 | |
| 117 | + | |
| 118 | +            quickstart = qs.getBoolean("enabled");
 | |
| 119 | + | |
| 120 | +            firewallEnabled = firewall.getBoolean("enabled");
 | |
| 121 | +            firewallAllowedPorts = firewall.getIntArray("allowed_ports");
 | |
| 122 | + | |
| 123 | +            proxyEnabled = proxy.getBoolean("enabled");
 | |
| 124 | +            proxyAddress = proxy.getString("address");
 | |
| 125 | +            proxyUsername = proxy.getString("username");
 | |
| 126 | +            proxyPassword = proxy.getString("password");
 | |
| 127 | +            proxyPort = proxy.getInt("port");
 | |
| 128 | +            proxyType = ProxyType.fromInt(proxy.getInt("type"));
 | |
| 129 | + | |
| 130 | +            loaded = true;
 | |
| 131 | +        } catch (Exception e) {
 | |
| 132 | +            Log.e("TorSettings", "bundle access error: " + e.toString(), e);
 | |
| 133 | +        }
 | |
| 134 | +    }
 | |
| 135 | + | |
| 136 | +    public GeckoBundle asGeckoBundle() {
 | |
| 137 | +        GeckoBundle bundle = new GeckoBundle();
 | |
| 138 | + | |
| 139 | +        GeckoBundle qs = new GeckoBundle();
 | |
| 140 | +        GeckoBundle bridges = new GeckoBundle();
 | |
| 141 | +        GeckoBundle proxy = new GeckoBundle();
 | |
| 142 | +        GeckoBundle firewall = new GeckoBundle();
 | |
| 143 | + | |
| 144 | +        bridges.putBoolean("enabled", bridgesEnabled);
 | |
| 145 | +        bridges.putInt("source", bridgesSource.toInt());
 | |
| 146 | +        bridges.putString("builtin_type", bridgesBuiltinType);
 | |
| 147 | +        bridges.putStringArray("bridge_strings", bridgeBridgeStrings);
 | |
| 148 | + | |
| 149 | +        qs.putBoolean("enabled", quickstart);
 | |
| 150 | + | |
| 151 | +        firewall.putBoolean("enabled", firewallEnabled);
 | |
| 152 | +        firewall.putIntArray("allowed_ports", firewallAllowedPorts);
 | |
| 153 | + | |
| 154 | +        proxy.putBoolean("enabled", proxyEnabled);
 | |
| 155 | +        proxy.putString("address", proxyAddress);
 | |
| 156 | +        proxy.putString("username", proxyUsername);
 | |
| 157 | +        proxy.putString("password", proxyPassword);
 | |
| 158 | +        proxy.putInt("port", proxyPort);
 | |
| 159 | +        proxy.putInt("type", proxyType.toInt());
 | |
| 160 | + | |
| 161 | +        bundle.putBundle("quickstart", qs);
 | |
| 162 | +        bundle.putBundle("bridges", bridges);
 | |
| 163 | +        bundle.putBundle("proxy", proxy);
 | |
| 164 | +        bundle.putBundle("firewall", firewall);
 | |
| 165 | + | |
| 166 | +        return bundle;
 | |
| 167 | +    }
 | |
| 168 | + | |
| 169 | +    public boolean isLoaded() {
 | |
| 170 | +        return this.loaded;
 | |
| 171 | +    }
 | |
| 172 | +} | 
| 1 | +package org.mozilla.geckoview.androidlegacysettings;
 | |
| 2 | + | |
| 3 | +import android.content.Context;
 | |
| 4 | +import android.content.SharedPreferences;
 | |
| 5 | +import org.mozilla.gecko.GeckoAppShell;
 | |
| 6 | + | |
| 7 | +import java.util.Locale;
 | |
| 8 | + | |
| 9 | +// tor-android-service utils/Prefs.java
 | |
| 10 | + | |
| 11 | +/* package */ class Prefs {
 | |
| 12 | +    private final static String PREF_BRIDGES_ENABLED = "pref_bridges_enabled";
 | |
| 13 | +    private final static String PREF_BRIDGES_LIST = "pref_bridges_list";
 | |
| 14 | + | |
| 15 | +    private static SharedPreferences prefs;
 | |
| 16 | + | |
| 17 | +    // OrbotConstants
 | |
| 18 | +    private final static String PREF_TOR_SHARED_PREFS = "org.torproject.android_preferences";
 | |
| 19 | + | |
| 20 | + | |
| 21 | +    // tor-android-service utils/TorServiceUtil.java
 | |
| 22 | + | |
| 23 | +    private static void setContext() {
 | |
| 24 | +        if (prefs == null) {
 | |
| 25 | +            prefs = GeckoAppShell.getApplicationContext().getSharedPreferences(PREF_TOR_SHARED_PREFS,
 | |
| 26 | +                    Context.MODE_MULTI_PROCESS);
 | |
| 27 | +        }
 | |
| 28 | +    }
 | |
| 29 | + | |
| 30 | +    public static boolean getBoolean(String key, boolean def) {
 | |
| 31 | +        setContext();
 | |
| 32 | +        return prefs.getBoolean(key, def);
 | |
| 33 | +    }
 | |
| 34 | + | |
| 35 | +    public static void putBoolean(String key, boolean value) {
 | |
| 36 | +        setContext();
 | |
| 37 | +        prefs.edit().putBoolean(key, value).apply();
 | |
| 38 | +    }
 | |
| 39 | + | |
| 40 | +    public static void putString(String key, String value) {
 | |
| 41 | +        setContext();
 | |
| 42 | +        prefs.edit().putString(key, value).apply();
 | |
| 43 | +    }
 | |
| 44 | + | |
| 45 | +    public static String getString(String key, String def) {
 | |
| 46 | +        setContext();
 | |
| 47 | +        return prefs.getString(key, def);
 | |
| 48 | +    }
 | |
| 49 | + | |
| 50 | +    public static boolean bridgesEnabled() {
 | |
| 51 | +        setContext();
 | |
| 52 | +        return prefs.getBoolean(PREF_BRIDGES_ENABLED, false);
 | |
| 53 | +    }
 | |
| 54 | + | |
| 55 | +    public static String getBridgesList() {
 | |
| 56 | +        setContext();
 | |
| 57 | +        // was "meek" for (Locale.getDefault().getLanguage().equals("fa")) and "obfs4" for the rest from a 2019 commit
 | |
| 58 | +        // but that has stopped representing a good default sometime since so not importing for new users
 | |
| 59 | +        String list = prefs.getString(PREF_BRIDGES_LIST, "");
 | |
| 60 | +        return list;
 | |
| 61 | +    }
 | |
| 62 | + | |
| 63 | + | |
| 64 | +} | 
| 1 | +package org.mozilla.geckoview.androidlegacysettings;
 | |
| 2 | + | |
| 3 | +import java.io.IOException;
 | |
| 4 | + | |
| 5 | +import android.content.SharedPreferences;
 | |
| 6 | + | |
| 7 | +import org.mozilla.gecko.GeckoAppShell;
 | |
| 8 | + | |
| 9 | +import org.mozilla.geckoview.TorSettings;
 | |
| 10 | + | |
| 11 | +public class TorLegacyAndroidSettings {
 | |
| 12 | + | |
| 13 | +    private static String PREF_USE_MOZ_PREFS = "tor_use_moz_prefs";
 | |
| 14 | + | |
| 15 | +    public static boolean unmigrated() {
 | |
| 16 | +        return !Prefs.getBoolean(PREF_USE_MOZ_PREFS, false);
 | |
| 17 | +    }
 | |
| 18 | + | |
| 19 | +    public static void setUnmigrated() {
 | |
| 20 | +        Prefs.putBoolean(PREF_USE_MOZ_PREFS, false);
 | |
| 21 | +    }
 | |
| 22 | + | |
| 23 | +    public static void setMigrated() {
 | |
| 24 | +        Prefs.putBoolean(PREF_USE_MOZ_PREFS, true);
 | |
| 25 | +    }
 | |
| 26 | + | |
| 27 | +    public static TorSettings loadTorSettings() {
 | |
| 28 | +        TorSettings settings = new TorSettings();
 | |
| 29 | + | |
| 30 | +        // always true, tor is enabled in TB
 | |
| 31 | +        settings.enabled = true;
 | |
| 32 | + | |
| 33 | +        // firefox-android disconnected quick start a while ago so it's untracked
 | |
| 34 | +        settings.quickstart = false;
 | |
| 35 | + | |
| 36 | +        settings.bridgesEnabled = Prefs.bridgesEnabled();
 | |
| 37 | + | |
| 38 | +        // tor-android-service CustomTorInstaller.java
 | |
| 39 | +/*
 | |
| 40 | +        BridgesList is an overloaded field, which can cause some confusion.
 | |
| 41 | +        The list can be:
 | |
| 42 | +          1) a filter like obfs4, meek, or snowflake OR
 | |
| 43 | +          2) it can be a custom bridge
 | |
| 44 | +        For (1), we just pass back all bridges, the filter will occur
 | |
| 45 | +          elsewhere in the library.
 | |
| 46 | +        For (2) we return the bridge list as a raw stream.
 | |
| 47 | +        If length is greater than 9, then we know this is a custom bridge
 | |
| 48 | +     */
 | |
| 49 | +        String userDefinedBridgeList = Prefs.getBridgesList();
 | |
| 50 | +        boolean userDefinedBridge = userDefinedBridgeList.length() > 9;
 | |
| 51 | +        // Terrible hack. Must keep in sync with topl::addBridgesFromResources.
 | |
| 52 | +        if (!userDefinedBridge) {
 | |
| 53 | +            settings.bridgesSource = TorSettings.BridgeSource.BuiltIn;
 | |
| 54 | +            switch (userDefinedBridgeList) {
 | |
| 55 | +                case "obfs4":
 | |
| 56 | +                    settings.bridgesBuiltinType = "objs4";
 | |
| 57 | +                    break;
 | |
| 58 | +                case "meek":
 | |
| 59 | +                    settings.bridgesBuiltinType = "meek_azure";
 | |
| 60 | +                    break;
 | |
| 61 | +                case "snowflake":
 | |
| 62 | +                    settings.bridgesBuiltinType = "snowflake";
 | |
| 63 | +                    break;
 | |
| 64 | +                default:
 | |
| 65 | +                    settings.bridgesSource = TorSettings.BridgeSource.Invalid;
 | |
| 66 | +                    break;
 | |
| 67 | +            }
 | |
| 68 | +        } else {
 | |
| 69 | +            settings.bridgesSource = TorSettings.BridgeSource.UserProvided; // user provided
 | |
| 70 | +            settings.bridgeBridgeStrings = userDefinedBridgeList.split("\r\n");
 | |
| 71 | +        }
 | |
| 72 | + | |
| 73 | +        // Tor Browser Android doesn't take proxy and firewall settings
 | |
| 74 | +        settings.proxyEnabled = false;
 | |
| 75 | + | |
| 76 | +        settings.firewallEnabled = false;
 | |
| 77 | +        settings.firewallAllowedPorts = new int[0];
 | |
| 78 | + | |
| 79 | +        return settings;
 | |
| 80 | +    }
 | |
| 81 | +}
 | |
| 82 | + | |
| 83 | + | |
| 84 | + | 
| ... | ... | @@ -8,6 +8,8 @@ const lazy = {}; | 
| 8 | 8 |  ChromeUtils.defineESModuleGetters(lazy, {
 | 
| 9 | 9 |    EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
 | 
| 10 | 10 |    TorConnect: "resource://gre/modules/TorConnect.sys.mjs",
 | 
| 11 | +  TorConnectTopics: "resource://gre/modules/TorConnect.sys.mjs",
 | |
| 12 | +  TorSettingsTopics: "resource://gre/modules/TorSettings.sys.mjs",
 | |
| 11 | 13 |    TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
 | 
| 12 | 14 |    TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
 | 
| 13 | 15 |  });
 | 
| ... | ... | @@ -23,11 +25,22 @@ const logger = new ConsoleAPI({ | 
| 23 | 25 |    prefix: "TorAndroidIntegration",
 | 
| 24 | 26 |  });
 | 
| 25 | 27 | |
| 28 | +const EmittedEvents = Object.freeze( {
 | |
| 29 | +  settingsReady: "GeckoView:Tor:SettingsReady",
 | |
| 30 | +  settingsChanged: "GeckoView:Tor:SettingsChanged",
 | |
| 31 | +});
 | |
| 32 | + | |
| 26 | 33 |  const ListenedEvents = Object.freeze({
 | 
| 27 | 34 |    settingsGet: "GeckoView:Tor:SettingsGet",
 | 
| 35 | +  // The data is passed directly to TorSettings.
 | |
| 28 | 36 |    settingsSet: "GeckoView:Tor:SettingsSet",
 | 
| 29 | 37 |    settingsApply: "GeckoView:Tor:SettingsApply",
 | 
| 30 | 38 |    settingsSave: "GeckoView:Tor:SettingsSave",
 | 
| 39 | +  bootstrapBegin: "GeckoView:Tor:BootstrapBegin",
 | |
| 40 | +  // Optionally takes a countryCode, as data.countryCode.
 | |
| 41 | +  bootstrapBeginAuto: "GeckoView:Tor:BootstrapBeginAuto",
 | |
| 42 | +  bootstrapCancel: "GeckoView:Tor:BootstrapCancel",
 | |
| 43 | +  bootstrapGetState: "GeckoView:Tor:BootstrapGetState",
 | |
| 31 | 44 |  });
 | 
| 32 | 45 | |
| 33 | 46 |  class TorAndroidIntegrationImpl {
 | 
| ... | ... | @@ -41,6 +54,14 @@ class TorAndroidIntegrationImpl { | 
| 41 | 54 | |
| 42 | 55 |      this.#bootstrapMethodReset();
 | 
| 43 | 56 |      Services.prefs.addObserver(Prefs.useNewBootstrap, this);
 | 
| 57 | + | |
| 58 | +    for (const topic in lazy.TorConnectTopics) {
 | |
| 59 | +      Services.obs.addObserver(this, lazy.TorConnectTopics[topic]);
 | |
| 60 | +    }
 | |
| 61 | + | |
| 62 | +    for (const topic in lazy.TorSettingsTopics) {
 | |
| 63 | +      Services.obs.addObserver(this, lazy.TorSettingsTopics[topic]);
 | |
| 64 | +    }
 | |
| 44 | 65 |    }
 | 
| 45 | 66 | |
| 46 | 67 |    async #initNewBootstrap() {
 | 
| ... | ... | @@ -67,6 +88,14 @@ class TorAndroidIntegrationImpl { | 
| 67 | 88 |            this.#bootstrapMethodReset();
 | 
| 68 | 89 |          }
 | 
| 69 | 90 |          break;
 | 
| 91 | +      case lazy.TorConnectTopics.StateChange:
 | |
| 92 | +        break;
 | |
| 93 | +      case lazy.TorSettingsTopics.Ready:
 | |
| 94 | +        lazy.EventDispatcher.instance.sendRequest({
 | |
| 95 | +          type: EmittedEvents.settingsReady,
 | |
| 96 | +          settings: lazy.TorSettings.getSettings(),
 | |
| 97 | +        });
 | |
| 98 | +        break;
 | |
| 70 | 99 |      }
 | 
| 71 | 100 |    }
 | 
| 72 | 101 | |
| ... | ... | @@ -74,24 +103,36 @@ class TorAndroidIntegrationImpl { | 
| 74 | 103 |      logger.debug(`Received event ${event}`, data);
 | 
| 75 | 104 |      try {
 | 
| 76 | 105 |        switch (event) {
 | 
| 77 | -        case settingsGet:
 | |
| 106 | +        case ListenedEvents.settingsGet:
 | |
| 78 | 107 |            callback?.onSuccess(lazy.TorSettings.getSettings());
 | 
| 79 | 108 |            return;
 | 
| 80 | -        case settingsSet:
 | |
| 109 | +        case ListenedEvents.settingsSet:
 | |
| 81 | 110 |            // This does not throw, so we do not have any way to report the error!
 | 
| 82 | 111 |            lazy.TorSettings.setSettings(data);
 | 
| 83 | 112 |            break;
 | 
| 84 | -        case settingsApply:
 | |
| 113 | +        case ListenedEvents.settingsApply:
 | |
| 85 | 114 |            await lazy.TorSettings.applySettings();
 | 
| 86 | 115 |            break;
 | 
| 87 | -        case settingsSave:
 | |
| 116 | +        case ListenedEvents.settingsSave:
 | |
| 88 | 117 |            await lazy.TorSettings.saveSettings();
 | 
| 89 | 118 |            break;
 | 
| 119 | +        case ListenedEvents.bootstrapBegin:
 | |
| 120 | +          lazy.TorConnect.beginBootstrap();
 | |
| 121 | +          break;
 | |
| 122 | +        case ListenedEvents.bootstrapBeginAuto:
 | |
| 123 | +          lazy.TorConnect.beginAutoBootstrap(data.countryCode);
 | |
| 124 | +          break;
 | |
| 125 | +        case ListenedEvents.bootstrapCancel:
 | |
| 126 | +          lazy.TorConnect.cancelBootstrap();
 | |
| 127 | +          break;
 | |
| 128 | +        case ListenedEvents.bootstrapGetState:
 | |
| 129 | +          callback?.onSuccess(lazy.TorConnect.state);
 | |
| 130 | +          return;
 | |
| 90 | 131 |        }
 | 
| 91 | 132 |        callback?.onSuccess();
 | 
| 92 | 133 |      } catch (e) {
 | 
| 93 | -      logger.error();
 | |
| 94 | -      callback?.sendError(e);
 | |
| 134 | +      logger.error(`Error while handling event ${event}`, e);
 | |
| 135 | +      callback?.onError(e);
 | |
| 95 | 136 |      }
 | 
| 96 | 137 |    }
 | 
| 97 | 138 |