[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [orbot/master] Initial work for V3 UI, DB Stuff, etc
commit 7ce301042cd9a56155b764f1e18f7396bc21b8fc
Author: bim <dsnake@xxxxxxxxxxxxxx>
Date: Sat Nov 14 20:59:02 2020 -0500
Initial work for V3 UI, DB Stuff, etc
---
app/src/main/AndroidManifest.xml | 14 +
.../org/torproject/android/OrbotMainActivity.java | 33 +-
.../ui/hiddenservices/HiddenServicesActivity.java | 6 +-
.../hiddenservices/adapters/OnionListAdapter.java | 7 +-
.../ui/hiddenservices/dialogs/HSDeleteDialog.java | 6 +-
.../permissions/PermissionManager.java | 14 +-
.../providers/HSContentProvider.java | 6 +-
.../DeleteOnionServiceDialogFragment.java | 41 +++
.../NewOnionServiceDialogFragment.java | 98 +++++
.../OnionServiceActionsDialogFragment.java | 93 +++++
.../OnionServiceContentProvider.java | 117 ++++++
.../ui/v3onionservice/OnionServiceDatabase.java | 38 ++
.../ui/v3onionservice/OnionServicesActivity.java | 98 +++++
.../ui/v3onionservice/OnionV3ListAdapter.java | 54 +++
app/src/main/res/menu/orbot_main.xml | 125 ++++---
app/src/main/res/values/strings.xml | 2 +
.../java/org/torproject/android/core/DiskUtils.kt | 8 +-
.../torproject/android/service/OrbotService.java | 408 +++++++--------------
.../android/service/TorServiceConstants.java | 2 +-
19 files changed, 797 insertions(+), 373 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a9db7611..7b81951f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -92,6 +92,15 @@
android:value=".OrbotMainActivity" />
</activity>
+ <activity
+ android:name=".ui.v3onionservice.OnionServicesActivity"
+ android:label="@string/hidden_services"
+ android:theme="@style/DefaultTheme">
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".OrbotMainActivity" />
+ </activity>
+
<activity
android:name=".ui.hiddenservices.ClientCookiesActivity"
android:label="@string/client_cookies"
@@ -123,6 +132,11 @@
android:authorities="org.torproject.android.ui.hiddenservices.providers"
android:exported="false" />
+ <provider
+ android:name=".ui.v3onionservice.OnionServiceContentProvider"
+ android:authorities="org.torproject.android.ui.v3onionservice"
+ android:exported="false" />
+
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="org.torproject.android.ui.hiddenservices.storage"
diff --git a/app/src/main/java/org/torproject/android/OrbotMainActivity.java b/app/src/main/java/org/torproject/android/OrbotMainActivity.java
index 1ee58106..abc87cc0 100644
--- a/app/src/main/java/org/torproject/android/OrbotMainActivity.java
+++ b/app/src/main/java/org/torproject/android/OrbotMainActivity.java
@@ -74,6 +74,7 @@ import org.torproject.android.ui.hiddenservices.permissions.PermissionManager;
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
import org.torproject.android.ui.onboarding.BridgeWizardActivity;
import org.torproject.android.ui.onboarding.OnboardingActivity;
+import org.torproject.android.ui.v3onionservice.OnionServicesActivity;
import java.io.File;
import java.io.UnsupportedEncodingException;
@@ -114,6 +115,9 @@ public class OrbotMainActivity extends AppCompatActivity implements OrbotConstan
private static final int MESSAGE_PORTS = 3;
private static final float ROTATE_FROM = 0.0f;
private static final float ROTATE_TO = 360.0f * 4f;// 3.141592654f * 32.0f;
+ // this is what takes messages or values from the callback threads or other non-mainUI threads
+ // and passes them back into the main UI thread for display to the user
+ private final Handler mStatusUpdateHandler = new MainActivityStatusUpdateHandler(this);
PulsatorLayout mPulsator;
AlertDialog aDialog = null;
/* Useful UI bits */
@@ -131,11 +135,6 @@ public class OrbotMainActivity extends AppCompatActivity implements OrbotConstan
/* Some tracking bits */
private String torStatus = null; //latest status reported from the tor service
private Intent lastStatusIntent; // the last ACTION_STATUS Intent received
- private SharedPreferences mPrefs = null;
- private boolean autoStartFromIntent = false;
- // this is what takes messages or values from the callback threads or other non-mainUI threads
- // and passes them back into the main UI thread for display to the user
- private final Handler mStatusUpdateHandler = new MainActivityStatusUpdateHandler(this);
/**
* The state and log info from {@link OrbotService} are sent to the UI here in
* the form of a local broadcast. Regular broadcasts can be sent by any app,
@@ -202,6 +201,8 @@ public class OrbotMainActivity extends AppCompatActivity implements OrbotConstan
}
}
};
+ private SharedPreferences mPrefs = null;
+ private boolean autoStartFromIntent = false;
private void migratePreferences() {
String hsPortString = mPrefs.getString("pref_hs_ports", "");
@@ -463,6 +464,8 @@ public class OrbotMainActivity extends AppCompatActivity implements OrbotConstan
}
}
+ } else if (item.getItemId() == R.id.menu_v3_onion_services) {
+ startActivity(new Intent(this, OnionServicesActivity.class));
} else if (item.getItemId() == R.id.menu_hidden_services) {
startActivity(new Intent(this, HiddenServicesActivity.class));
} else if (item.getItemId() == R.id.menu_client_cookies) {
@@ -534,12 +537,8 @@ public class OrbotMainActivity extends AppCompatActivity implements OrbotConstan
sendIntentToService(ACTION_START_VPN);
}
- private void enableHiddenServicePort(
- String hsName, final int hsPort, int hsRemotePort,
- final String backupToPackage, final Uri hsKeyPath,
- final Boolean authCookie
- ) {
-
+ private void enableHiddenServicePort(String hsName, final int hsPort, int hsRemotePort,
+ final String backupToPackage, final Uri hsKeyPath, final Boolean authCookie) {
String onionHostname = null;
if (hsName == null)
@@ -687,9 +686,9 @@ public class OrbotMainActivity extends AppCompatActivity implements OrbotConstan
};
String requestMsg = getString(R.string.hidden_service_request, String.valueOf(hiddenServicePort));
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setMessage(requestMsg).setPositiveButton("Allow", dialogClickListener)
- .setNegativeButton("Deny", dialogClickListener).show();
+ new AlertDialog.Builder(this).setMessage(requestMsg)
+ .setPositiveButton(R.string.allow, dialogClickListener)
+ .setNegativeButton(R.string.deny, dialogClickListener).show();
return; //don't null the setIntent() as we need it later
@@ -1103,12 +1102,14 @@ public class OrbotMainActivity extends AppCompatActivity implements OrbotConstan
imgStatus.startAnimation(rotation);
lblStatus.setText(getString(R.string.newnym));
break;
- case STATUS_STARTING: return; // tor is starting up, a new identity isn't needed
+ case STATUS_STARTING:
+ return; // tor is starting up, a new identity isn't needed
case STATUS_OFF:
case STATUS_STOPPING:
startTor();
break;
- default: break;
+ default:
+ break;
}
}
diff --git a/app/src/main/java/org/torproject/android/ui/hiddenservices/HiddenServicesActivity.java b/app/src/main/java/org/torproject/android/ui/hiddenservices/HiddenServicesActivity.java
index 6c780dde..d7429181 100644
--- a/app/src/main/java/org/torproject/android/ui/hiddenservices/HiddenServicesActivity.java
+++ b/app/src/main/java/org/torproject/android/ui/hiddenservices/HiddenServicesActivity.java
@@ -61,13 +61,9 @@ public class HiddenServicesActivity extends AppCompatActivity {
mResolver = getContentResolver();
fab = findViewById(R.id.fab);
- fab.setOnClickListener(view -> {
- HSDataDialog dialog = new HSDataDialog();
- dialog.show(getSupportFragmentManager(), "HSDataDialog");
- });
+ fab.setOnClickListener(view -> new HSDataDialog().show(getSupportFragmentManager(), "HSDataDialog"));
mAdapter = new OnionListAdapter(this, mResolver.query(HSContentProvider.CONTENT_URI, HSContentProvider.PROJECTION, mWhere, null, null), 0);
-
mResolver.registerContentObserver(HSContentProvider.CONTENT_URI, true, new HSObserver(new Handler()));
ListView onion_list = findViewById(R.id.onion_list);
diff --git a/app/src/main/java/org/torproject/android/ui/hiddenservices/adapters/OnionListAdapter.java b/app/src/main/java/org/torproject/android/ui/hiddenservices/adapters/OnionListAdapter.java
index d4e39ece..e3983cfd 100644
--- a/app/src/main/java/org/torproject/android/ui/hiddenservices/adapters/OnionListAdapter.java
+++ b/app/src/main/java/org/torproject/android/ui/hiddenservices/adapters/OnionListAdapter.java
@@ -25,8 +25,7 @@ public class OnionListAdapter extends CursorAdapter {
}
@Override
- public void bindView(View view, Context context, Cursor cursor) {
- final Context mContext = context;
+ public void bindView(View view, final Context context, Cursor cursor) {
int id = cursor.getInt(cursor.getColumnIndex(HSContentProvider.HiddenService._ID));
final String where = HSContentProvider.HiddenService._ID + "=" + id;
@@ -40,12 +39,12 @@ public class OnionListAdapter extends CursorAdapter {
SwitchCompat enabled = view.findViewById(R.id.hs_switch);
enabled.setChecked(cursor.getInt(cursor.getColumnIndex(HSContentProvider.HiddenService.ENABLED)) == 1);
enabled.setOnCheckedChangeListener((buttonView, isChecked) -> {
- ContentResolver resolver = mContext.getContentResolver();
+ ContentResolver resolver = context.getContentResolver();
ContentValues fields = new ContentValues();
fields.put(HSContentProvider.HiddenService.ENABLED, isChecked);
resolver.update(HSContentProvider.CONTENT_URI, fields, where, null);
- Toast.makeText(mContext, R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG).show();
+ Toast.makeText(context, R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG).show();
});
}
diff --git a/app/src/main/java/org/torproject/android/ui/hiddenservices/dialogs/HSDeleteDialog.java b/app/src/main/java/org/torproject/android/ui/hiddenservices/dialogs/HSDeleteDialog.java
index d26141a0..f4a96226 100644
--- a/app/src/main/java/org/torproject/android/ui/hiddenservices/dialogs/HSDeleteDialog.java
+++ b/app/src/main/java/org/torproject/android/ui/hiddenservices/dialogs/HSDeleteDialog.java
@@ -20,11 +20,9 @@ public class HSDeleteDialog extends DialogFragment {
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
- final Bundle arguments = getArguments();
- final Context context = getContext();
- return new AlertDialog.Builder(context)
+ return new AlertDialog.Builder(getContext())
.setTitle(R.string.confirm_service_deletion)
- .setPositiveButton(android.R.string.ok, (dialog, which) -> doDelete(arguments, context))
+ .setPositiveButton(android.R.string.ok, (dialog, which) -> doDelete(getArguments(), getContext()))
.setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.cancel())
.create();
}
diff --git a/app/src/main/java/org/torproject/android/ui/hiddenservices/permissions/PermissionManager.java b/app/src/main/java/org/torproject/android/ui/hiddenservices/permissions/PermissionManager.java
index 6d181967..52c5dc20 100644
--- a/app/src/main/java/org/torproject/android/ui/hiddenservices/permissions/PermissionManager.java
+++ b/app/src/main/java/org/torproject/android/ui/hiddenservices/permissions/PermissionManager.java
@@ -23,9 +23,8 @@ public class PermissionManager {
@TargetApi(Build.VERSION_CODES.M)
public static void requestBatteryPermissions(FragmentActivity activity, Context context) {
- final Context mContext = context;
- final String packageName = mContext.getPackageName();
- PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ final String packageName = context.getPackageName();
+ PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (pm.isIgnoringBatteryOptimizations(packageName))
return;
@@ -37,18 +36,15 @@ public class PermissionManager {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
- mContext.startActivity(intent);
+ context.startActivity(intent);
}).show();
}
@TargetApi(Build.VERSION_CODES.M)
public static void requestDropBatteryPermissions(FragmentActivity activity, Context context) {
- final Context mContext = context;
-
- final String packageName = context.getPackageName();
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- if (!pm.isIgnoringBatteryOptimizations(packageName))
+ if (!pm.isIgnoringBatteryOptimizations(context.getPackageName()))
return;
Snackbar.make(activity.findViewById(android.R.id.content),
@@ -57,7 +53,7 @@ public class PermissionManager {
v -> {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
- mContext.startActivity(intent);
+ context.startActivity(intent);
}).show();
}
}
diff --git a/app/src/main/java/org/torproject/android/ui/hiddenservices/providers/HSContentProvider.java b/app/src/main/java/org/torproject/android/ui/hiddenservices/providers/HSContentProvider.java
index 87cad0ad..79b566fd 100644
--- a/app/src/main/java/org/torproject/android/ui/hiddenservices/providers/HSContentProvider.java
+++ b/app/src/main/java/org/torproject/android/ui/hiddenservices/providers/HSContentProvider.java
@@ -27,8 +27,7 @@ public class HSContentProvider extends ContentProvider {
HiddenService.ENABLED
};
private static final String AUTH = "org.torproject.android.ui.hiddenservices.providers";
- public static final Uri CONTENT_URI =
- Uri.parse("content://" + AUTH + "/hs");
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTH + "/hs");
//UriMatcher
private static final int ONIONS = 1;
private static final int ONION_ID = 2;
@@ -61,8 +60,7 @@ public class HSContentProvider extends ContentProvider {
SQLiteDatabase db = mServervices.getReadableDatabase();
- return db.query(HSDatabase.HS_DATA_TABLE_NAME, projection, where,
- selectionArgs, null, null, sortOrder);
+ return db.query(HSDatabase.HS_DATA_TABLE_NAME, projection, where, selectionArgs, null, null, sortOrder);
}
@Nullable
diff --git a/app/src/main/java/org/torproject/android/ui/v3onionservice/DeleteOnionServiceDialogFragment.java b/app/src/main/java/org/torproject/android/ui/v3onionservice/DeleteOnionServiceDialogFragment.java
new file mode 100644
index 00000000..5028e8f3
--- /dev/null
+++ b/app/src/main/java/org/torproject/android/ui/v3onionservice/DeleteOnionServiceDialogFragment.java
@@ -0,0 +1,41 @@
+package org.torproject.android.ui.v3onionservice;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.DialogFragment;
+
+import org.torproject.android.R;
+import org.torproject.android.core.DiskUtils;
+import org.torproject.android.service.TorServiceConstants;
+import org.torproject.android.ui.hiddenservices.HiddenServicesActivity;
+
+import java.io.File;
+
+public class DeleteOnionServiceDialogFragment extends DialogFragment {
+ DeleteOnionServiceDialogFragment(Bundle arguments) {
+ super();
+ setArguments(arguments);
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getContext())
+ .setTitle(R.string.confirm_service_deletion)
+ .setPositiveButton(android.R.string.ok, (dialog, which) -> doDelete(getArguments(), getContext()))
+ .setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.cancel())
+ .create();
+ }
+
+ private void doDelete(Bundle arguments, Context context) {
+ context.getContentResolver().delete(OnionServiceContentProvider.CONTENT_URI, OnionServiceContentProvider.OnionService._ID + '=' + arguments.getInt(OnionServicesActivity.BUNDLE_KEY_ID), null);
+ String base = context.getFilesDir().getAbsolutePath() + "/" + TorServiceConstants.ONION_SERVICES_DIR;
+ DiskUtils.recursivelyDeleteDirectory(new File(base, "v3" + arguments.getString(OnionServicesActivity.BUNDLE_KEY_PORT)));
+ }
+
+}
diff --git a/app/src/main/java/org/torproject/android/ui/v3onionservice/NewOnionServiceDialogFragment.java b/app/src/main/java/org/torproject/android/ui/v3onionservice/NewOnionServiceDialogFragment.java
new file mode 100644
index 00000000..1929eb72
--- /dev/null
+++ b/app/src/main/java/org/torproject/android/ui/v3onionservice/NewOnionServiceDialogFragment.java
@@ -0,0 +1,98 @@
+package org.torproject.android.ui.v3onionservice;
+
+import android.app.Dialog;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.DialogFragment;
+
+import org.torproject.android.R;
+
+public class NewOnionServiceDialogFragment extends DialogFragment {
+
+ private EditText etServer, etLocalPort, etOnionPort;
+ private TextWatcher inputValidator;
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ inputValidator.afterTextChanged(null); // initially disable positive button
+ }
+
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final View dialogView = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_data_dialog, null);
+ dialogView.findViewById(R.id.hsAuth).setVisibility(View.GONE);
+ etServer = dialogView.findViewById(R.id.hsName);
+ etLocalPort = dialogView.findViewById(R.id.hsLocalPort);
+ etOnionPort = dialogView.findViewById(R.id.hsOnionPort);
+
+ AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.hidden_services)
+ .setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.cancel())
+ .setPositiveButton(R.string.save, (dialog, which) -> doSave(getContext()))
+ .setView(dialogView)
+ .create();
+
+
+ inputValidator = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) { // no-op
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) { //no-op
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ Button btn = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+ try {
+ int localPort = Integer.parseInt(etLocalPort.getText().toString());
+ int onionPort = Integer.parseInt(etOnionPort.getText().toString());
+ btn.setEnabled(checkInput(localPort, onionPort));
+ } catch (NumberFormatException nfe) {
+ btn.setEnabled(false);
+ }
+ }
+ };
+
+ etServer.addTextChangedListener(inputValidator);
+ etLocalPort.addTextChangedListener(inputValidator);
+ etOnionPort.addTextChangedListener(inputValidator);
+ return alertDialog;
+ }
+
+ private boolean checkInput(int local, int remote) {
+ if ((local < 1 || local > 65535) || (remote < 1 || remote > 65535)) return false;
+ return !TextUtils.isEmpty(etServer.getText().toString().trim());
+ }
+
+ private void doSave(Context context) {
+ String serverName = etServer.getText().toString().trim();
+ int localPort = Integer.parseInt(etLocalPort.getText().toString());
+ int onionPort = Integer.parseInt(etOnionPort.getText().toString());
+ ContentValues fields = new ContentValues();
+ fields.put(OnionServiceContentProvider.OnionService.NAME, serverName);
+ fields.put(OnionServiceContentProvider.OnionService.PORT, localPort);
+ fields.put(OnionServiceContentProvider.OnionService.ONION_PORT, onionPort);
+ fields.put(OnionServiceContentProvider.OnionService.CREATED_BY_USER, 1);
+ ContentResolver cr = getContext().getContentResolver();
+ cr.insert(OnionServiceContentProvider.CONTENT_URI, fields);
+ Toast.makeText(context, R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG).show();
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceActionsDialogFragment.java b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceActionsDialogFragment.java
new file mode 100644
index 00000000..822a45f2
--- /dev/null
+++ b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceActionsDialogFragment.java
@@ -0,0 +1,93 @@
+package org.torproject.android.ui.v3onionservice;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.DialogFragment;
+
+import org.torproject.android.R;
+import org.torproject.android.core.ClipboardUtils;
+import org.torproject.android.core.DiskUtils;
+import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
+
+import java.io.File;
+
+public class OnionServiceActionsDialogFragment extends DialogFragment {
+
+ private static final int REQUEST_CODE_WRITE_FILE = 343;
+
+ OnionServiceActionsDialogFragment(Bundle arguments) {
+ super();
+ setArguments(arguments);
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Bundle arguments = getArguments();
+ AlertDialog ad = new AlertDialog.Builder(getActivity())
+ .setItems(new CharSequence[]{
+ getString(R.string.copy_address_to_clipboard),
+ getString(R.string.backup_service),
+ getString(R.string.delete_service)}, null)
+ .setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.dismiss())
+ .setTitle(R.string.hidden_services)
+ .create();
+
+ // done this way so we can startActivityForResult on backup without the dialog vanishing
+ ad.getListView().setOnItemClickListener((parent, view, position, id) -> {
+ if (position == 0) doCopy(arguments, getContext());
+ else if (position == 1) doBackup(arguments, getContext());
+ else if (position == 2)
+ new DeleteOnionServiceDialogFragment(arguments).show(getFragmentManager(), DeleteOnionServiceDialogFragment.class.getSimpleName());
+ if (position != 1) dismiss();
+ });
+ return ad;
+ }
+
+ private void doCopy(Bundle arguments, Context context) {
+ String onion = arguments.getString(OnionServicesActivity.BUNDLE_KEY_DOMAIN);
+ if (onion == null)
+ Toast.makeText(context, R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG).show();
+ else
+ ClipboardUtils.copyToClipboard("onion", onion, getString(R.string.done), context);
+ }
+
+ private void doBackup(Bundle arguments, Context context) {
+ String filename = "onion_service" + arguments.getInt(OnionServicesActivity.BUNDLE_KEY_PORT) + ".zip";
+ if (arguments.getString(OnionServicesActivity.BUNDLE_KEY_DOMAIN) == null) {
+ Toast.makeText(context, R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG).show();
+ return;
+ }
+ if (DiskUtils.supportsStorageAccessFramework()) {
+ Intent createFileIntent = DiskUtils.createWriteFileIntent(filename, "application/zip");
+ startActivityForResult(createFileIntent, REQUEST_CODE_WRITE_FILE);
+ } else { // APIs 16, 17, 18
+ attemptToWriteBackup(Uri.fromFile(new File(DiskUtils.getOrCreateLegacyBackupDir(), filename)));
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CODE_WRITE_FILE && resultCode == Activity.RESULT_OK) {
+ if (data != null) {
+ attemptToWriteBackup(data.getData());
+ }
+ }
+ }
+
+ private void attemptToWriteBackup(Uri outputFile) {
+ BackupUtils backupUtils = new BackupUtils(getContext());
+// String backup = backupUtils.createZipBackup(port, outputFile); TODO need to break apart backup logic for v2 and v3 onions
+ Toast.makeText(getContext(), backup != null ? R.string.backup_saved_at_external_storage : R.string.error, Toast.LENGTH_LONG).show();
+ dismiss();
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceContentProvider.java b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceContentProvider.java
new file mode 100644
index 00000000..d61f7935
--- /dev/null
+++ b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceContentProvider.java
@@ -0,0 +1,117 @@
+package org.torproject.android.ui.v3onionservice;
+
+import android.content.ContentProvider;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.provider.BaseColumns;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public class OnionServiceContentProvider extends ContentProvider {
+
+ public static final String[] PROJECTION = {
+ OnionService._ID,
+ OnionService.NAME,
+ OnionService.PORT,
+ OnionService.DOMAIN,
+ OnionService.ONION_PORT,
+ OnionService.CREATED_BY_USER,
+ OnionService.ENABLED
+ };
+
+ private static final int ONIONS = 1, ONION_ID = 2;
+ private static final String AUTH = "org.torproject.android.ui.v3onionservice";
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTH + "/v3");
+ private static final UriMatcher uriMatcher;
+
+ static {
+ uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+ uriMatcher.addURI(AUTH, "v3", ONIONS);
+ uriMatcher.addURI(AUTH, "v3/#", ONION_ID);
+ }
+
+ private OnionServiceDatabase mDatabase;
+
+ @Override
+ public boolean onCreate() {
+ mDatabase = new OnionServiceDatabase(getContext());
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
+ String where = selection;
+ if (uriMatcher.match(uri) == ONION_ID) {
+ where = "_id=" + uri.getLastPathSegment();
+ }
+
+ SQLiteDatabase db = mDatabase.getReadableDatabase();
+ return db.query(OnionServiceDatabase.ONION_SERVICE_TABLE_NAME, projection, where, selectionArgs, null, null, sortOrder);
+ }
+
+ @Nullable
+ @Override
+ public String getType(@NonNull Uri uri) {
+ int match = uriMatcher.match(uri);
+ switch (match) {
+ case ONIONS:
+ return "vnd.android.cursor.dir/vnd.torproject.onions";
+ case ONION_ID:
+ return "vnd.android.cursor.item/vnd.torproject.onion";
+ default:
+ return null;
+ }
+ }
+
+ @Nullable
+ @Override
+ public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
+ SQLiteDatabase db = mDatabase.getWritableDatabase();
+ long regId = db.insert(OnionServiceDatabase.ONION_SERVICE_TABLE_NAME, null, values);
+ getContext().getContentResolver().notifyChange(CONTENT_URI, null);
+ return ContentUris.withAppendedId(CONTENT_URI, regId);
+ }
+
+ @Override
+ public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
+ if (uriMatcher.match(uri) == ONION_ID) {
+ selection = "_id=" + uri.getLastPathSegment();
+ }
+ SQLiteDatabase db = mDatabase.getWritableDatabase();
+ int rows = db.delete(OnionServiceDatabase.ONION_SERVICE_TABLE_NAME, selection, selectionArgs);
+ getContext().getContentResolver().notifyChange(CONTENT_URI, null);
+ return rows;
+ }
+
+ @Override
+ public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
+ SQLiteDatabase db = mDatabase.getWritableDatabase();
+ String where = selection;
+ if (uriMatcher.match(uri) == ONION_ID) {
+ where = "_id=" + uri.getLastPathSegment();
+ }
+
+ int rows = db.update(OnionServiceDatabase.ONION_SERVICE_TABLE_NAME, values, where, null);
+ getContext().getContentResolver().notifyChange(CONTENT_URI, null);
+ return rows;
+ }
+
+ public static final class OnionService implements BaseColumns {
+ public static final String NAME = "name";
+ public static final String PORT = "port";
+ public static final String ONION_PORT = "onion_port";
+ public static final String DOMAIN = "domain";
+ public static final String CREATED_BY_USER = "created_by_user";
+ public static final String ENABLED = "enabled";
+
+ private OnionService() { // no-op
+ }
+ }
+
+}
diff --git a/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceDatabase.java b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceDatabase.java
new file mode 100644
index 00000000..f2a812e8
--- /dev/null
+++ b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceDatabase.java
@@ -0,0 +1,38 @@
+package org.torproject.android.ui.v3onionservice;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+public class OnionServiceDatabase extends SQLiteOpenHelper {
+
+ static final String DATABASE_NAME = "onion_service",
+ ONION_SERVICE_TABLE_NAME = "onion_services";
+ private static final int DATABASE_VERSION = 1;
+
+ private static final String ONION_SERVICES_CREATE_SQL =
+ "CREATE TABLE " + ONION_SERVICE_TABLE_NAME + " (" +
+ "_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ "name TEXT, " +
+ "domain TEXT, " +
+ "onion_port INTEGER, " +
+ "created_by_user INTEGER DEFAULT 0, " +
+ "enabled INTEGER DEFAULT 1, " +
+ "port INTEGER);";
+
+ public OnionServiceDatabase(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL(ONION_SERVICES_CREATE_SQL);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+
+ }
+
+
+}
diff --git a/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServicesActivity.java b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServicesActivity.java
new file mode 100644
index 00000000..3be04731
--- /dev/null
+++ b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServicesActivity.java
@@ -0,0 +1,98 @@
+package org.torproject.android.ui.v3onionservice;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ListView;
+import android.widget.RadioButton;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+
+import org.torproject.android.R;
+import org.torproject.android.core.DiskUtils;
+import org.torproject.android.core.LocaleHelper;
+
+public class OnionServicesActivity extends AppCompatActivity {
+
+ static final String BUNDLE_KEY_ID = "id", BUNDLE_KEY_PORT = "port", BUNDLE_KEY_DOMAIN = "domain";
+ private static final String WHERE_SELECTION_CLAUSE = OnionServiceContentProvider.OnionService.CREATED_BY_USER + "=1";
+ private RadioButton radioShowUserServices, radioShowAppServices;
+ private FloatingActionButton fab;
+ private ContentResolver mContentResolver;
+ private OnionV3ListAdapter mAdapter;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ setContentView(R.layout.layout_hs_list_view);
+
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ fab = findViewById(R.id.fab);
+ fab.setOnClickListener(v -> new NewOnionServiceDialogFragment().show(getSupportFragmentManager(), "NewOnionServiceDialogFragment"));
+
+ mContentResolver = getContentResolver();
+ mAdapter = new OnionV3ListAdapter(this, mContentResolver.query(OnionServiceContentProvider.CONTENT_URI, OnionServiceContentProvider.PROJECTION, WHERE_SELECTION_CLAUSE, null, null), 0);
+ mContentResolver.registerContentObserver(OnionServiceContentProvider.CONTENT_URI, true, new OnionServiceObserver(new Handler()));
+
+ ListView onionList = findViewById(R.id.onion_list);
+ onionList.setAdapter(mAdapter);
+ onionList.setOnItemClickListener((parent, view, position, id) -> {
+ Cursor item = (Cursor) parent.getItemAtPosition(position);
+ Bundle arguments = new Bundle();
+ arguments.putInt(BUNDLE_KEY_ID, item.getInt(item.getColumnIndex(OnionServiceContentProvider.OnionService._ID)));
+ arguments.putString(BUNDLE_KEY_PORT, item.getString(item.getColumnIndex(OnionServiceContentProvider.OnionService.PORT)));
+ arguments.putString(BUNDLE_KEY_DOMAIN, item.getString(item.getColumnIndex(OnionServiceContentProvider.OnionService.DOMAIN)));
+ OnionServiceActionsDialogFragment dialog = new OnionServiceActionsDialogFragment(arguments);
+ dialog.show(getSupportFragmentManager(), OnionServiceActionsDialogFragment.class.getSimpleName());
+ });
+ }
+
+ @Override
+ protected void attachBaseContext(Context base) {
+ super.attachBaseContext(LocaleHelper.onAttach(base));
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.hs_menu, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.menu_restore_backup) {
+ if (DiskUtils.supportsStorageAccessFramework()) {
+
+ } else { // 16, 17, 18
+
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private class OnionServiceObserver extends ContentObserver {
+
+ OnionServiceObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mAdapter.changeCursor(mContentResolver.query(OnionServiceContentProvider.CONTENT_URI, OnionServiceContentProvider.PROJECTION, WHERE_SELECTION_CLAUSE, null, null));
+ // todo battery optimization stuff if lollipop or higher and running onion services
+ }
+ }
+
+
+}
diff --git a/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionV3ListAdapter.java b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionV3ListAdapter.java
new file mode 100644
index 00000000..c0489d3c
--- /dev/null
+++ b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionV3ListAdapter.java
@@ -0,0 +1,54 @@
+package org.torproject.android.ui.v3onionservice;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CursorAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.appcompat.widget.SwitchCompat;
+
+import org.torproject.android.R;
+import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
+
+public class OnionV3ListAdapter extends CursorAdapter {
+
+ private final LayoutInflater mLayoutInflater;
+
+ OnionV3ListAdapter(Context context, Cursor cursor, int flags) {
+ super(context, cursor, flags);
+ mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mLayoutInflater.inflate(R.layout.layout_hs_list_item, parent, false);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ int id = cursor.getInt(cursor.getColumnIndex(OnionServiceContentProvider.OnionService._ID));
+ final String where = OnionServiceContentProvider.OnionService._ID + "=" + id;
+ TextView port = view.findViewById(R.id.hs_port);
+ port.setText(cursor.getString(cursor.getColumnIndex(OnionServiceContentProvider.OnionService.PORT)));
+ TextView name = view.findViewById(R.id.hs_name);
+ name.setText(cursor.getString(cursor.getColumnIndex(OnionServiceContentProvider.OnionService.NAME)));
+ TextView domain = view.findViewById(R.id.hs_onion);
+ domain.setText(cursor.getString(cursor.getColumnIndex(OnionServiceContentProvider.OnionService.DOMAIN)));
+
+ SwitchCompat enabled = view.findViewById(R.id.hs_switch);
+ enabled.setChecked(cursor.getInt(cursor.getColumnIndex(OnionServiceContentProvider.OnionService.ENABLED)) == 1);
+ enabled.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ ContentResolver resolver = context.getContentResolver();
+ ContentValues fields = new ContentValues();
+ fields.put(OnionServiceContentProvider.OnionService.ENABLED, isChecked);
+ resolver.update(OnionServiceContentProvider.CONTENT_URI, fields, where, null);
+ Toast.makeText(context, R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG).show();
+ });
+ }
+}
diff --git a/app/src/main/res/menu/orbot_main.xml b/app/src/main/res/menu/orbot_main.xml
index 6359ebdc..2fb0ad4e 100644
--- a/app/src/main/res/menu/orbot_main.xml
+++ b/app/src/main/res/menu/orbot_main.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
/*
* Copyright (C) 2008 Esmertec AG.
* Copyright (C) 2008 The Android Open Source Project
@@ -18,77 +17,83 @@
*/
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:yourapp="http://schemas.android.com/apk/res-auto"
- >
+ xmlns:yourapp="http://schemas.android.com/apk/res-auto">
- <item android:id="@+id/menu_newnym"
- android:title="@string/menu_new_identity"
+ <item
+ android:id="@+id/menu_newnym"
android:icon="@drawable/ic_refresh_white_24dp"
- yourapp:showAsAction="always"
- />
+ android:title="@string/menu_new_identity"
+ yourapp:showAsAction="always" />
- <item android:id="@+id/menu_settings"
+ <item
+ android:id="@+id/menu_settings"
+ android:icon="@drawable/ic_action_settings"
android:title="@string/menu_settings"
- android:icon="@drawable/ic_action_settings"
- yourapp:showAsAction="never"
- />
+ yourapp:showAsAction="never" />
- <item
+ <item
android:title="@string/menu_qr"
- yourapp:showAsAction="never"
- >
- <menu>
- <item android:id="@+id/menu_scan"
- android:title="@string/menu_scan"
- yourapp:showAsAction="never"
- />
-
- <item android:id="@+id/menu_share_bridge"
- android:title="@string/menu_share_bridge"
- yourapp:showAsAction="never"
- />
- </menu>
- </item>
+ yourapp:showAsAction="never">
+ <menu>
+ <item
+ android:id="@+id/menu_scan"
+ android:title="@string/menu_scan"
+ yourapp:showAsAction="never" />
+
+ <item
+ android:id="@+id/menu_share_bridge"
+ android:title="@string/menu_share_bridge"
+ yourapp:showAsAction="never" />
+ </menu>
+ </item>
<item
android:title="@string/menu_hidden_services"
yourapp:showAsAction="never">
- <menu>
- <item android:id="@+id/menu_hidden_services"
- android:title="@string/hosted_services"
- yourapp:showAsAction="never"
- />
-
- <item android:id="@+id/menu_client_cookies"
- android:title="@string/client_cookies"
- yourapp:showAsAction="never"
- />
- </menu>
+ <menu>
+
+ <item
+ android:id="@+id/menu_v3_onion_services"
+ android:title="@string/hidden_services"
+ yourapp:showAsAction="never" />
+
+ <item
+ android:id="@+id/menu_hidden_services"
+ android:title="@string/hosted_services"
+ yourapp:showAsAction="never" />
+
+ <item
+ android:id="@+id/menu_client_cookies"
+ android:title="@string/client_cookies"
+ yourapp:showAsAction="never" />
+ </menu>
</item>
-
- <!--
- <item android:id="@+id/menu_promo_apps"
- android:title="@string/menu_promo_apps"
- android:icon="@drawable/ic_menu_goto"
- yourapp:showAsAction="never"
-
- />
- -->
-
- <item android:id="@+id/menu_about"
+
+ <!--
+ <item android:id="@+id/menu_promo_apps"
+ android:title="@string/menu_promo_apps"
+ android:icon="@drawable/ic_menu_goto"
+ yourapp:showAsAction="never"
+
+ />
+ -->
+
+ <item
+ android:id="@+id/menu_about"
+ android:icon="@drawable/ic_menu_about"
android:title="@string/menu_about"
- android:icon="@drawable/ic_menu_about"
- yourapp:showAsAction="never"
-
- />
-
- <item android:id="@+id/menu_exit"
+ yourapp:showAsAction="never"
+
+ />
+
+ <item
+ android:id="@+id/menu_exit"
+ android:icon="@drawable/ic_menu_exit"
android:title="@string/menu_exit"
- android:icon="@drawable/ic_menu_exit"
- yourapp:showAsAction="never"
-
- />
-
+ yourapp:showAsAction="never"
+
+ />
+
</menu>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c9fc00a6..0501adf8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -101,6 +101,8 @@
<string name="obfsproxy_version">Obfs4proxy: https://github.com/Yawning/obfs4</string>
<string name="openssl_version">OpenSSL: http://www.openssl.org</string>
<string name="hidden_service_request">An app wants to open onion server port %1$s to the Tor network. This is safe if you trust the app.</string>
+ <string name="allow">Allow</string>
+ <string name="deny">Deny</string>
<string name="found_existing_tor_process">found existing Tor process…</string>
<string name="something_bad_happened">Something bad happened. Check the log</string>
<string name="unable_to_read_hidden_service_name">unable to read onion service name</string>
diff --git a/appcore/src/main/java/org/torproject/android/core/DiskUtils.kt b/appcore/src/main/java/org/torproject/android/core/DiskUtils.kt
index 7e741695..be74aa4e 100644
--- a/appcore/src/main/java/org/torproject/android/core/DiskUtils.kt
+++ b/appcore/src/main/java/org/torproject/android/core/DiskUtils.kt
@@ -68,9 +68,15 @@ object DiskUtils {
@JvmStatic
fun getOrCreateLegacyBackupDir(): File? {
if (Environment.MEDIA_MOUNTED != Environment.getExternalStorageState()) return null
- val dir = File(Environment.getExternalStorageDirectory(), "Orbot")
+ val dir = File(Environment.getExternalStorageDirectory(), )
return if (!dir.isDirectory && !dir.mkdirs()) null else dir
}
+ @JvmStatic
+ fun recursivelyDeleteDirectory(directory: File) : Boolean {
+ val contents = directory.listFiles()
+ contents?.forEach { recursivelyDeleteDirectory(it) }
+ return directory.delete()
+ }
}
\ No newline at end of file
diff --git a/orbotservice/src/main/java/org/torproject/android/service/OrbotService.java b/orbotservice/src/main/java/org/torproject/android/service/OrbotService.java
index 36dcb0ae..10acf5de 100644
--- a/orbotservice/src/main/java/org/torproject/android/service/OrbotService.java
+++ b/orbotservice/src/main/java/org/torproject/android/service/OrbotService.java
@@ -39,7 +39,6 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.jaredrummler.android.shell.CommandResult;
-import net.freehaven.tor.control.ConfigEntry;
import net.freehaven.tor.control.TorControlCommands;
import net.freehaven.tor.control.TorControlConnection;
@@ -72,7 +71,6 @@ import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.List;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.concurrent.ExecutorService;
@@ -84,14 +82,36 @@ import IPtProxy.IPtProxy;
public class OrbotService extends VpnService implements TorServiceConstants, OrbotConstants {
public final static String BINARY_TOR_VERSION = org.torproject.android.binary.TorServiceConstants.BINARY_TOR_VERSION;
- private final static int CONTROL_SOCKET_TIMEOUT = 60000;
static final int NOTIFY_ID = 1;
+ private final static int CONTROL_SOCKET_TIMEOUT = 60000;
private static final int ERROR_NOTIFY_ID = 3;
private static final int HS_NOTIFY_ID = 4;
- private static final Uri HS_CONTENT_URI = Uri.parse("content://org.torproject.android.ui.hiddenservices.providers/hs");
+ private static final Uri V2_HS_CONTENT_URI = Uri.parse("content://org.torproject.android.ui.hiddenservices.providers/hs");
+ private static final Uri V3_ONION_SERVICES_CONTENT_URI = Uri.parse("content://org.torproject.android.ui.v3onionservice/v3");
private static final Uri COOKIE_CONTENT_URI = Uri.parse("content://org.torproject.android.ui.hiddenservices.providers.cookie/cookie");
private final static String NOTIFICATION_CHANNEL_ID = "orbot_channel_1";
- private final static String RESET_STRING = "=\"\"";
+ private static final String[] LEGACY_V2_ONION_SERVICE_PROJECTION = new String[]{
+ OnionService._ID,
+ OnionService.NAME,
+ OnionService.DOMAIN,
+ OnionService.PORT,
+ OnionService.AUTH_COOKIE,
+ OnionService.AUTH_COOKIE_VALUE,
+ OnionService.ONION_PORT,
+ OnionService.ENABLED};
+ private static final String[] V3_ONION_SERVICE_PROJECTION = new String[]{
+ OnionService._ID,
+ OnionService.NAME,
+ OnionService.DOMAIN,
+ OnionService.PORT,
+ OnionService.ONION_PORT,
+ OnionService.ENABLED,
+ };
+ private static final String[] LEGACY_COOKIE_PROJECTION = new String[]{
+ ClientCookie._ID,
+ ClientCookie.DOMAIN,
+ ClientCookie.AUTH_COOKIE_VALUE,
+ ClientCookie.ENABLED};
public static int mPortSOCKS = -1;
public static int mPortHTTP = -1;
public static int mPortDns = TOR_DNS_PORT_DEFAULT;
@@ -100,6 +120,7 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
public static File appCacheHome;
public static File fileTor;
public static File fileTorRc;
+ private final ExecutorService mExecutor = Executors.newCachedThreadPool();
boolean mIsLollipop = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
TorEventHandler mEventHandler;
OrbotVpnManager mVpnManager;
@@ -110,29 +131,12 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
private String mCurrentStatus = STATUS_OFF;
private TorControlConnection conn = null;
private int mLastProcessId = -1;
- private ArrayList<String> configBuffer = null;
- private ArrayList<String> resetBuffer = null;
private File fileControlPort, filePid;
private NotificationManager mNotificationManager = null;
private NotificationCompat.Builder mNotifyBuilder;
private boolean mNotificationShowing = false;
- private final ExecutorService mExecutor = Executors.newCachedThreadPool();
- private File mHSBasePath;
+ private File mHSBasePath, mV3OnionBasePath;
private ArrayList<Bridge> alBridges = null;
- private final String[] hsProjection = new String[]{
- HiddenService._ID,
- HiddenService.NAME,
- HiddenService.DOMAIN,
- HiddenService.PORT,
- HiddenService.AUTH_COOKIE,
- HiddenService.AUTH_COOKIE_VALUE,
- HiddenService.ONION_PORT,
- HiddenService.ENABLED};
- private final String[] cookieProjection = new String[]{
- ClientCookie._ID,
- ClientCookie.DOMAIN,
- ClientCookie.AUTH_COOKIE_VALUE,
- ClientCookie.ENABLED};
/**
* @param bridgeList bridges that were manually entered into Orbot settings
@@ -144,13 +148,16 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
return bridgeList.split("\\n");
}
- public void debug(String msg) {
+ private static boolean useIPtProxy() {
+ String bridgeList = Prefs.getBridgesList();
+ return bridgeList.contains("obfs3") || bridgeList.contains("obfs4") || bridgeList.contains("meek");
+ }
+ public void debug(String msg) {
Log.d(OrbotConstants.TAG, msg);
if (Prefs.useDebugLogging()) {
sendCallbackLogMessage(msg);
-
}
}
@@ -264,7 +271,6 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
}
public int onStartCommand(Intent intent, int flags, int startId) {
-
showToolbarNotification("", NOTIFY_ID, R.drawable.ic_stat_tor);
if (intent != null)
@@ -347,7 +353,6 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
/**
* if someone stops during startup, we may have to wait for the conn port to be setup, so we can properly shutdown tor
- * @throws Exception
*/
private void stopTorDaemon(boolean waitForConnection) throws Exception {
@@ -372,7 +377,10 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
if (!waitForConnection)
break;
- try { Thread.sleep(3000);}catch (Exception e){}
+ try {
+ Thread.sleep(3000);
+ } catch (Exception e) {
+ }
}
}
@@ -384,13 +392,6 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
} catch (IOException e) {
e.printStackTrace();
}
- /**
- // if that fails, try again using native utils
- try {
- killProcess(fileTor, "-1"); // this is -HUP
- } catch (Exception e) {
- e.printStackTrace();
- }**/
}
protected void logNotice(String msg) {
@@ -439,14 +440,14 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
fileControlPort = new File(getFilesDir(), TOR_CONTROL_PORT_FILE);
filePid = new File(getFilesDir(), TOR_PID_FILE);
- mHSBasePath = new File(
- getFilesDir().getAbsolutePath(),
- TorServiceConstants.HIDDEN_SERVICES_DIR
- );
-
+ mHSBasePath = new File(getFilesDir().getAbsolutePath(), TorServiceConstants.HIDDEN_SERVICES_DIR);
if (!mHSBasePath.isDirectory())
mHSBasePath.mkdirs();
+ mV3OnionBasePath = new File(getFilesDir().getAbsolutePath(), TorServiceConstants.ONION_SERVICES_DIR);
+ if (!mV3OnionBasePath.isDirectory())
+ mV3OnionBasePath.mkdirs();
+
mEventHandler = new TorEventHandler(this);
if (mNotificationManager == null) {
@@ -501,12 +502,12 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
private boolean pluggableTransportInstall() {
- File fileCacheDir = new File(getCacheDir(),"pt");
+ File fileCacheDir = new File(getCacheDir(), "pt");
if (!fileCacheDir.exists())
fileCacheDir.mkdir();
IPtProxy.setStateLocation(fileCacheDir.getAbsolutePath());
String fileTestState = IPtProxy.getStateLocation();
- debug ("IPtProxy state: " + fileTestState);
+ debug("IPtProxy state: " + fileTestState);
return false;
}
@@ -657,13 +658,10 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
if (isPortUsed) //the specified port is not available, so let Tor find one instead
port++;
}
-
-
return port + "";
}
return portString;
-
}
public boolean updateTorConfigCustom(File fileTorRcCustom, String extraLines) throws IOException, TimeoutException {
@@ -710,8 +708,6 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
* The entire process for starting tor and related services is run from this method.
*/
private void startTor() {
-
-
try {
// STATUS_STARTING is set in onCreate()
@@ -734,9 +730,8 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
String torProcId = conn.getInfo("process/pid");
if (!TextUtils.isEmpty(torProcId))
mLastProcessId = Integer.parseInt(torProcId);
- }
- else {
- if (fileControlPort!=null && fileControlPort.exists())
+ } else {
+ if (fileControlPort != null && fileControlPort.exists())
findExistingTorDaemon();
}
@@ -765,9 +760,14 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
if (success) {
try {
- updateOnionNames();
+ updateLegacyV2OnionNames();
+ } catch (SecurityException se) {
+ logNotice("unable to upload legacy v2 onion names");
+ }
+ try {
+ updateV3OnionNames();
} catch (SecurityException se) {
- logNotice("unable to upload onion names");
+ logNotice("unable to upload v3 onion names");
}
}
@@ -777,21 +777,48 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
showToolbarNotification(
getString(R.string.unable_to_start_tor) + ": " + e.getMessage(),
ERROR_NOTIFY_ID, R.drawable.ic_stat_notifyerr);
+ }
+ }
+ private void updateV3OnionNames() throws SecurityException {
+ ContentResolver contentResolver = getApplicationContext().getContentResolver();
+ Cursor onionServices = contentResolver.query(V3_ONION_SERVICES_CONTENT_URI, null, null, null, null);
+ if (onionServices != null) {
+ try {
+ while (onionServices.moveToNext()) {
+ String domain = onionServices.getString(onionServices.getColumnIndex(OnionService.DOMAIN));
+ int localPort = onionServices.getInt(onionServices.getColumnIndex(OnionService.PORT));
+
+ if (domain == null || TextUtils.isEmpty(domain)) {
+ String v3OnionDirPath = new File(mV3OnionBasePath.getAbsolutePath(), "v3" + localPort).getCanonicalPath();
+ File hostname = new File(v3OnionDirPath, "hostname");
+ if (hostname.exists()) {
+ domain = Utils.readString(new FileInputStream(hostname)).trim();
+ ContentValues fields = new ContentValues();
+ fields.put(OnionService.DOMAIN, domain);
+ contentResolver.update(V3_ONION_SERVICES_CONTENT_URI, fields, "port=" + localPort, null);
+ }
+ }
+
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ onionServices.close();
}
}
- private void updateOnionNames() throws SecurityException {
+ private void updateLegacyV2OnionNames() throws SecurityException {
// Tor is running, update new .onion names at db
ContentResolver mCR = getApplicationContext().getContentResolver();
- Cursor hidden_services = mCR.query(HS_CONTENT_URI, hsProjection, null, null, null);
+ Cursor hidden_services = mCR.query(V2_HS_CONTENT_URI, LEGACY_V2_ONION_SERVICE_PROJECTION, null, null, null);
if (hidden_services != null) {
try {
while (hidden_services.moveToNext()) {
- String HSDomain = hidden_services.getString(hidden_services.getColumnIndex(HiddenService.DOMAIN));
- Integer HSLocalPort = hidden_services.getInt(hidden_services.getColumnIndex(HiddenService.PORT));
- Integer HSAuthCookie = hidden_services.getInt(hidden_services.getColumnIndex(HiddenService.AUTH_COOKIE));
- String HSAuthCookieValue = hidden_services.getString(hidden_services.getColumnIndex(HiddenService.AUTH_COOKIE_VALUE));
+ String HSDomain = hidden_services.getString(hidden_services.getColumnIndex(OnionService.DOMAIN));
+ Integer HSLocalPort = hidden_services.getInt(hidden_services.getColumnIndex(OnionService.PORT));
+ Integer HSAuthCookie = hidden_services.getInt(hidden_services.getColumnIndex(OnionService.AUTH_COOKIE));
+ String HSAuthCookieValue = hidden_services.getString(hidden_services.getColumnIndex(OnionService.AUTH_COOKIE_VALUE));
// Update only new domains or restored from backup with auth cookie
if ((HSDomain == null || HSDomain.length() < 1) || (HSAuthCookie == 1 && (HSAuthCookieValue == null || HSAuthCookieValue.length() < 1))) {
@@ -806,10 +833,10 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
if (HSAuthCookie == 1) {
String[] aux = onionHostname.split(" ");
onionHostname = aux[0];
- fields.put(HiddenService.AUTH_COOKIE_VALUE, aux[1]);
+ fields.put(OnionService.AUTH_COOKIE_VALUE, aux[1]);
}
- fields.put(HiddenService.DOMAIN, onionHostname);
- mCR.update(HS_CONTENT_URI, fields, "port=" + HSLocalPort, null);
+ fields.put(OnionService.DOMAIN, onionHostname);
+ mCR.update(V2_HS_CONTENT_URI, fields, "port=" + HSLocalPort, null);
} catch (FileNotFoundException e) {
logException("unable to read onion hostname file", e);
showToolbarNotification(getString(R.string.unable_to_read_hidden_service_name), HS_NOTIFY_ID, R.drawable.ic_stat_notifyerr);
@@ -832,8 +859,6 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
}
private boolean runTorShellCmd() throws Exception {
- boolean result = true;
-
File fileTorrcCustom = updateTorrcCustomFile();
//make sure Tor exists and we can execute it
@@ -868,9 +893,7 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
exitCode = exec(torCmdString, false);
} catch (Exception e) {
logNotice("Tor was unable to start: " + e.getMessage());
-
throw new Exception("Tor was unable to start: " + e.getMessage());
-
}
if (exitCode != 0) {
@@ -885,13 +908,11 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
logNotice(getString(R.string.couldn_t_start_tor_process_) + "; exit=" + exitCode);
throw new Exception(getString(R.string.couldn_t_start_tor_process_) + "; exit=" + exitCode);
} else {
-
logNotice("Tor started; process id=" + mLastProcessId);
- result = true;
}
}
- return result;
+ return true;
}
protected void exec(Runnable runn) {
@@ -918,23 +939,17 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
while (conn == null && attempt++ < maxTries && (mCurrentStatus != STATUS_OFF)) {
try {
-
controlPort = getControlPort();
-
if (controlPort != -1) {
logNotice(getString(R.string.connecting_to_control_port) + controlPort);
-
-
break;
}
} catch (Exception ce) {
conn = null;
// logException( "Error connecting to Tor local control port: " + ce.getMessage(),ce);
-
}
-
try {
// logNotice("waiting...");
Thread.sleep(2000);
@@ -942,27 +957,19 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
}
}
-
if (controlPort != -1) {
Socket torConnSocket = new Socket(IP_LOCALHOST, controlPort);
torConnSocket.setSoTimeout(CONTROL_SOCKET_TIMEOUT);
-
conn = new TorControlConnection(torConnSocket);
-
conn.launchThread(true);//is daemon
-
}
if (conn != null) {
-
logNotice("SUCCESS connected to Tor control port.");
File fileCookie = new File(appCacheHome, TOR_CONTROL_COOKIE);
if (fileCookie.exists()) {
-
- // We extend NullEventHandler so that we don't need to provide empty
- // implementations for all the events we don't care about.
logNotice("adding control port event handler");
conn.setEventHandler(mEventHandler);
@@ -1019,7 +1026,6 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
}
sendCallbackPorts(mPortSOCKS, mPortHTTP, mPortDns, mPortTrans);
-
setTorNetworkEnabled(true);
return Integer.parseInt(torProcId);
@@ -1066,20 +1072,6 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
return result;
}
- /**
- * Returns the port number that the HTTP proxy is running on
- */
- public int getHTTPPort() {
- return mPortHTTP;
- }
-
- /**
- * Returns the port number that the HTTP proxy is running on
- */
- public int getSOCKSPort() {
- return mPortSOCKS;
- }
-
public String getInfo(String key) {
try {
if (conn != null) {
@@ -1092,71 +1084,11 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
return null;
}
- public String getConfiguration(String name) {
- try {
- if (conn != null) {
- StringBuffer result = new StringBuffer();
-
- List<ConfigEntry> listCe = conn.getConf(name);
-
- Iterator<ConfigEntry> itCe = listCe.iterator();
- ConfigEntry ce;
-
-
- while (itCe.hasNext()) {
- ce = itCe.next();
-
- result.append(ce.key);
- result.append(' ');
- result.append(ce.value);
- result.append('\n');
- }
-
- return result.toString();
- }
- } catch (Exception ioe) {
-
- logException("Unable to get Tor configuration: " + ioe.getMessage(), ioe);
- }
-
- return null;
- }
-
- /**
- * Set configuration
- **/
- public boolean updateConfiguration(String name, String value, boolean saveToDisk) {
-
-
- if (configBuffer == null)
- configBuffer = new ArrayList<>();
-
- if (resetBuffer == null)
- resetBuffer = new ArrayList<>();
-
- if (value == null || value.length() == 0) {
- resetBuffer.add(name + RESET_STRING);
-
- } else {
-
- String sbConf = name +
- ' ' +
- value;
- configBuffer.add(sbConf);
- }
-
- return false;
- }
-
-
public void setTorNetworkEnabled(final boolean isEnabled) throws IOException {
-
- //it is possible to not have a connection yet, and someone might try to newnym
- if (conn != null) {
+ if (conn != null) { // it is possible to not have a connection yet, and someone might try to newnym
new Thread() {
public void run() {
try {
-
final String newValue = isEnabled ? "0" : "1";
conn.setConf("DisableNetwork", newValue);
} catch (Exception ioe) {
@@ -1165,7 +1097,6 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
}
}.start();
}
-
}
public void sendSignalActive() {
@@ -1179,8 +1110,7 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
}
public void newIdentity() {
- //it is possible to not have a connection yet, and someone might try to newnym
- if (conn != null) {
+ if (conn != null) { // it is possible to not have a connection yet, and someone might try to newnym
new Thread() {
public void run() {
try {
@@ -1199,49 +1129,6 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
}
}
- public boolean saveConfiguration() {
- try {
- if (conn != null) {
-
- if (resetBuffer != null && resetBuffer.size() > 0) {
- for (String value : configBuffer) {
-
- // debug("removing torrc conf: " + value);
-
-
- }
-
- // conn.resetConf(resetBuffer);
- resetBuffer = null;
- }
-
- if (configBuffer != null && configBuffer.size() > 0) {
-
- for (String value : configBuffer) {
-
- debug("Setting torrc conf: " + value);
-
-
- }
-
- conn.setConf(configBuffer);
-
- configBuffer = null;
- }
-
- // Flush the configuration to disk.
- //this is doing bad things right now NF 22/07/10
- //conn.saveConf();
-
- return true;
- }
- } catch (Exception ioe) {
- logException("Unable to update Tor configuration: " + ioe.getMessage(), ioe);
- }
-
- return false;
- }
-
protected void sendCallbackBandwidth(long upload, long download, long written, long read) {
Intent intent = new Intent(LOCAL_ACTION_BANDWIDTH);
@@ -1255,11 +1142,8 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
}
private void sendCallbackLogMessage(final String logMessage) {
-
mHandler.post(() -> {
-
- Intent intent = new Intent(LOCAL_ACTION_LOG);
- // You can also include some extra data.
+ Intent intent = new Intent(LOCAL_ACTION_LOG); // You can also include some extra data.
intent.putExtra(LOCAL_EXTRA_LOG, logMessage);
intent.putExtra(EXTRA_STATUS, mCurrentStatus);
@@ -1269,9 +1153,7 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
}
private void sendCallbackPorts(int socksPort, int httpPort, int dnsPort, int transPort) {
-
- Intent intent = new Intent(LOCAL_ACTION_PORTS);
- // You can also include some extra data.
+ Intent intent = new Intent(LOCAL_ACTION_PORTS); // You can also include some extra data.
intent.putExtra(EXTRA_SOCKS_PROXY_PORT, socksPort);
intent.putExtra(EXTRA_HTTP_PROXY_PORT, httpPort);
intent.putExtra(EXTRA_DNS_PORT, dnsPort);
@@ -1355,26 +1237,19 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
private StringBuffer processSettingsImpl(StringBuffer extraLines) throws IOException {
logNotice(getString(R.string.updating_settings_in_tor_service));
-
SharedPreferences prefs = Prefs.getSharedPrefs(getApplicationContext());
boolean useBridges = Prefs.bridgesEnabled();
-
boolean becomeRelay = prefs.getBoolean(OrbotConstants.PREF_OR, false);
boolean ReachableAddresses = prefs.getBoolean(OrbotConstants.PREF_REACHABLE_ADDRESSES, false);
-
boolean enableStrictNodes = prefs.getBoolean("pref_strict_nodes", false);
String entranceNodes = prefs.getString("pref_entrance_nodes", "");
String exitNodes = prefs.getString("pref_exit_nodes", "");
String excludeNodes = prefs.getString("pref_exclude_nodes", "");
if (!useBridges) {
-
extraLines.append("UseBridges 0").append('\n');
-
- if (Prefs.useVpn()) //set the proxy here if we aren't using a bridge
- {
-
+ if (Prefs.useVpn()) { //set the proxy here if we aren't using a bridge
if (!mIsLollipop) {
String proxyType = "socks5";
extraLines.append(proxyType + "Proxy" + ' ' + OrbotVpnManager.sSocksProxyLocalhost + ':' + OrbotVpnManager.sSocksProxyServerPort).append('\n');
@@ -1400,15 +1275,12 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
} else if (proxyPass != null)
extraLines.append(proxyType + "ProxyAuthenticator" + ' ' + proxyUser + ':' + proxyPort).append('\n');
-
-
}
}
}
} else {
loadBridgeDefaults();
-
extraLines.append("UseBridges 1").append('\n');
// extraLines.append("UpdateBridgesFromAuthority 1").append('\n');
@@ -1445,12 +1317,9 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
extraLines.append("Bridge ");
extraLines.append(bridgeLine);
extraLines.append("\n");
-
}
-
}
-
//only apply GeoIP if you need it
File fileGeoIP = new File(appBinHome, GEOIP_ASSET_KEY);
File fileGeoIP6 = new File(appBinHome, GEOIP6_ASSET_KEY);
@@ -1473,16 +1342,12 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
try {
if (ReachableAddresses) {
- String ReachableAddressesPorts =
- prefs.getString(OrbotConstants.PREF_REACHABLE_ADDRESSES_PORTS, "*:80,*:443");
-
+ String ReachableAddressesPorts = prefs.getString(OrbotConstants.PREF_REACHABLE_ADDRESSES_PORTS, "*:80,*:443");
extraLines.append("ReachableAddresses" + ' ' + ReachableAddressesPorts).append('\n');
-
}
} catch (Exception e) {
showToolbarNotification(getString(R.string.your_reachableaddresses_settings_caused_an_exception_), ERROR_NOTIFY_ID, R.drawable.ic_stat_notifyerr);
-
return null;
}
@@ -1490,7 +1355,6 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
if (becomeRelay && (!useBridges) && (!ReachableAddresses)) {
int ORPort = Integer.parseInt(prefs.getString(OrbotConstants.PREF_OR_PORT, "9001"));
String nickname = prefs.getString(OrbotConstants.PREF_OR_NICKNAME, "Orbot");
-
String dnsFile = writeDNSFile();
extraLines.append("ServerDNSResolvConfFile" + ' ' + dnsFile).append('\n');
@@ -1501,33 +1365,56 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
}
} catch (Exception e) {
showToolbarNotification(getString(R.string.your_relay_settings_caused_an_exception_), ERROR_NOTIFY_ID, R.drawable.ic_stat_notifyerr);
-
-
return null;
}
- ContentResolver mCR = getApplicationContext().getContentResolver();
+ ContentResolver contentResolver = getApplicationContext().getContentResolver();
+ addV3OnionServicesToTorrc(extraLines, contentResolver);
+// addV2HiddenServicesToTorrc(extraLines, contentResolver);
+// addV2ClientCookiesToTorrc(extraLines, contentResolver);
+ return extraLines;
+ }
+ private void addV3OnionServicesToTorrc(StringBuffer torrc, ContentResolver contentResolver) {
try {
- /* ---- Hidden Services ---- */
- Cursor hidden_services = mCR.query(HS_CONTENT_URI, hsProjection, HiddenService.ENABLED + "=1", null, null);
+ Cursor onionServices = contentResolver.query(V3_ONION_SERVICES_CONTENT_URI, V3_ONION_SERVICE_PROJECTION, OnionService.ENABLED + "=1", null, null);
+ if (onionServices != null) {
+ while (onionServices.moveToNext()) {
+ int localPort = onionServices.getInt(onionServices.getColumnIndex(OnionService.PORT));
+ int onionPort = onionServices.getInt(onionServices.getColumnIndex(OnionService.ONION_PORT));
+ String v3DirPath = new File(mV3OnionBasePath.getAbsolutePath(), "v3" + localPort).getCanonicalPath();
+ torrc.append("HiddenServiceDir ").append(v3DirPath).append("\n");
+ torrc.append("HiddenServiceVersion 3").append("\n");
+ torrc.append("HiddenServicePort ").append(onionPort).append(" 127.0.0.1:").append(localPort).append("\n");
+ }
+ onionServices.close();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, e.getLocalizedMessage());
+ }
+ }
+
+ // todo needs modifications to set hidden service version back after doing v3 stuff...
+ private void addV2HiddenServicesToTorrc(StringBuffer torrc, ContentResolver contentResolver) {
+ try {
+ Cursor hidden_services = contentResolver.query(V2_HS_CONTENT_URI, LEGACY_V2_ONION_SERVICE_PROJECTION, OnionService.ENABLED + "=1", null, null);
if (hidden_services != null) {
try {
while (hidden_services.moveToNext()) {
- String HSname = hidden_services.getString(hidden_services.getColumnIndex(HiddenService.NAME));
- Integer HSLocalPort = hidden_services.getInt(hidden_services.getColumnIndex(HiddenService.PORT));
- Integer HSOnionPort = hidden_services.getInt(hidden_services.getColumnIndex(HiddenService.ONION_PORT));
- Integer HSAuthCookie = hidden_services.getInt(hidden_services.getColumnIndex(HiddenService.AUTH_COOKIE));
+ String HSname = hidden_services.getString(hidden_services.getColumnIndex(OnionService.NAME));
+ int HSLocalPort = hidden_services.getInt(hidden_services.getColumnIndex(OnionService.PORT));
+ int HSOnionPort = hidden_services.getInt(hidden_services.getColumnIndex(OnionService.ONION_PORT));
+ int HSAuthCookie = hidden_services.getInt(hidden_services.getColumnIndex(OnionService.AUTH_COOKIE));
String hsDirPath = new File(mHSBasePath.getAbsolutePath(), "hs" + HSLocalPort).getCanonicalPath();
debug("Adding hidden service on port: " + HSLocalPort);
- extraLines.append("HiddenServiceDir" + ' ' + hsDirPath).append('\n');
- extraLines.append("HiddenServicePort" + ' ' + HSOnionPort + " 127.0.0.1:" + HSLocalPort).append('\n');
- extraLines.append("HiddenServiceVersion 2").append('\n');
+ torrc.append("HiddenServiceDir" + ' ' + hsDirPath).append('\n');
+ torrc.append("HiddenServicePort" + ' ' + HSOnionPort + " 127.0.0.1:" + HSLocalPort).append('\n');
+ torrc.append("HiddenServiceVersion 2").append('\n');
if (HSAuthCookie == 1)
- extraLines.append("HiddenServiceAuthorizeClient stealth " + HSname).append('\n');
+ torrc.append("HiddenServiceAuthorizeClient stealth " + HSname).append('\n');
}
} catch (NumberFormatException e) {
Log.e(OrbotConstants.TAG, "error parsing hsport", e);
@@ -1539,28 +1426,25 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
}
} catch (SecurityException se) {
}
+ }
+ private void addV2ClientCookiesToTorrc(StringBuffer torrc, ContentResolver contentResolver) {
try {
-
- /* ---- Client Cookies ---- */
- Cursor client_cookies = mCR.query(COOKIE_CONTENT_URI, cookieProjection, ClientCookie.ENABLED + "=1", null, null);
+ Cursor client_cookies = contentResolver.query(COOKIE_CONTENT_URI, LEGACY_COOKIE_PROJECTION, ClientCookie.ENABLED + "=1", null, null);
if (client_cookies != null) {
try {
while (client_cookies.moveToNext()) {
String domain = client_cookies.getString(client_cookies.getColumnIndex(ClientCookie.DOMAIN));
String cookie = client_cookies.getString(client_cookies.getColumnIndex(ClientCookie.AUTH_COOKIE_VALUE));
- extraLines.append("HidServAuth" + ' ' + domain + ' ' + cookie).append('\n');
+ torrc.append("HidServAuth" + ' ' + domain + ' ' + cookie).append('\n');
}
} catch (Exception e) {
Log.e(OrbotConstants.TAG, "error starting share server", e);
}
-
client_cookies.close();
}
} catch (SecurityException se) {
}
-
- return extraLines;
}
//using Google DNS for now as the public DNS server
@@ -1614,7 +1498,6 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind");
- //do nothing here
return super.onBind(intent); // invoking super class will call onRevoke() when appropriate
}
@@ -1676,8 +1559,7 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
alBridges = new ArrayList<>();
try {
- BufferedReader in =
- new BufferedReader(new InputStreamReader(getResources().openRawResource(R.raw.bridges), "UTF-8"));
+ BufferedReader in = new BufferedReader(new InputStreamReader(getResources().openRawResource(R.raw.bridges), "UTF-8"));
String str;
while ((str = in.readLine()) != null) {
@@ -1692,9 +1574,7 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
sbConfig.append(st.nextToken()).append(' ');
b.config = sbConfig.toString().trim();
-
alBridges.add(b);
-
}
in.close();
@@ -1702,7 +1582,6 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
e.printStackTrace();
}
}
-
}
private void getBridges(String type, StringBuffer extraLines) {
@@ -1728,20 +1607,18 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
break;
}
}
-
}
- public static final class HiddenService implements BaseColumns {
+ public static final class OnionService implements BaseColumns {
public static final String NAME = "name";
public static final String PORT = "port";
public static final String ONION_PORT = "onion_port";
public static final String DOMAIN = "domain";
public static final String AUTH_COOKIE = "auth_cookie";
public static final String AUTH_COOKIE_VALUE = "auth_cookie_value";
- public static final String CREATED_BY_USER = "created_by_user";
public static final String ENABLED = "enabled";
- private HiddenService() {
+ private OnionService() {
}
}
@@ -1768,7 +1645,6 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
}
public void run() {
-
String action = mIntent.getAction();
if (!TextUtils.isEmpty(action)) {
@@ -1804,11 +1680,8 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
if (mVpnManager != null && (!mVpnManager.isStarted())) {
//start VPN here
Intent vpnIntent = VpnService.prepare(OrbotService.this);
- if (vpnIntent == null) //then we can run the VPN
- {
+ if (vpnIntent == null) { //then we can run the VPN
mVpnManager.handleIntent(new Builder(), mIntent);
-
-
}
}
@@ -1828,9 +1701,7 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
} else if (action.equals(CMD_ACTIVE)) {
sendSignalActive();
} else if (action.equals(CMD_SET_EXIT)) {
-
setExitNode(mIntent.getStringExtra("exit"));
-
} else {
Log.w(OrbotConstants.TAG, "unhandled OrbotService Intent: " + action);
}
@@ -1852,5 +1723,4 @@ public class OrbotService extends VpnService implements TorServiceConstants, Orb
}
}
}
-
}
diff --git a/orbotservice/src/main/java/org/torproject/android/service/TorServiceConstants.java b/orbotservice/src/main/java/org/torproject/android/service/TorServiceConstants.java
index f1769687..915f149d 100644
--- a/orbotservice/src/main/java/org/torproject/android/service/TorServiceConstants.java
+++ b/orbotservice/src/main/java/org/torproject/android/service/TorServiceConstants.java
@@ -112,6 +112,6 @@ public interface TorServiceConstants {
String OBFSCLIENT_ASSET_KEY = "obfs4proxy";
String HIDDEN_SERVICES_DIR = "hidden_services";
-
+ String ONION_SERVICES_DIR = "v3_onion_services";
}
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits