Merge branch 'hidden_services' of https://github.com/arrase/orbot into arrase-hidden_services
This commit is contained in:
commit
26b9199378
|
@ -30,5 +30,5 @@ dependencies {
|
|||
compile project(':orbotservice')
|
||||
compile 'com.android.support:support-v4:23.4.0'
|
||||
compile 'com.android.support:appcompat-v7:23.4.0'
|
||||
|
||||
compile 'com.android.support:design:23.4.0'
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.torproject.android"
|
||||
android:versionName="15.2.0-RC-8-multi"
|
||||
android:versionCode="15208000"
|
||||
android:installLocation="auto"
|
||||
>
|
||||
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="23"/>
|
||||
package="org.torproject.android"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="15208000"
|
||||
android:versionName="15.2.0-RC-8-multi">
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="16"
|
||||
android:targetSdkVersion="23" />
|
||||
<!--
|
||||
<permission android:name="org.torproject.android.MANAGE_TOR"
|
||||
android:label="@string/permission_manage_tor_label"
|
||||
|
@ -13,125 +15,164 @@
|
|||
android:protectionLevel="signature"/>
|
||||
|
||||
<uses-permission android:name="org.torproject.android.MANAGE_TOR"/>
|
||||
-->
|
||||
-->
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
|
||||
<application android:name="org.torproject.android.OrbotApp" android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:description="@string/app_description"
|
||||
android:configChanges="locale|orientation|screenSize"
|
||||
android:theme="@style/DefaultTheme"
|
||||
android:allowBackup="false"
|
||||
android:allowClearUserData="true"
|
||||
android:largeHeap="false"
|
||||
android:hardwareAccelerated="false"
|
||||
<android:uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
|
||||
>
|
||||
|
||||
<activity android:name=".OrbotMainActivity"
|
||||
<application
|
||||
android:name=".OrbotApp"
|
||||
android:allowBackup="false"
|
||||
android:allowClearUserData="true"
|
||||
android:configChanges="locale|orientation|screenSize"
|
||||
android:description="@string/app_description"
|
||||
android:hardwareAccelerated="false"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:largeHeap="false"
|
||||
android:theme="@style/DefaultTheme">
|
||||
<activity
|
||||
android:name=".OrbotMainActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleTop"
|
||||
>
|
||||
android:launchMode="singleTop">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="bridge" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<intent-filter>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<intent-filter>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<action android:name="org.torproject.android.START_TOR" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
<!--
|
||||
This is for ensuring the background service still runs when/if the app is swiped away
|
||||
-->
|
||||
<activity
|
||||
android:name=".service.util.DummyActivity"
|
||||
android:theme="@android:style/Theme.Translucent"
|
||||
android:enabled="true"
|
||||
android:allowTaskReparenting="true"
|
||||
android:noHistory="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:alwaysRetainTaskState="false"
|
||||
android:stateNotNeeded="true"
|
||||
android:clearTaskOnLaunch="true"
|
||||
android:finishOnTaskLaunch="true"
|
||||
|
||||
/>
|
||||
|
||||
<activity
|
||||
android:name=".vpn.VPNEnableActivity" android:label="@string/app_name" android:exported="false"
|
||||
/>
|
||||
|
||||
|
||||
<activity android:name="org.torproject.android.ui.PromoAppsActivity" android:exported="false"/>
|
||||
|
||||
|
||||
<activity android:name=".settings.SettingsPreferences" android:label="@string/app_name"/>
|
||||
<activity android:name=".ui.AppManager" android:label="@string/app_name"
|
||||
android:theme="@style/Theme.AppCompat"
|
||||
/>
|
||||
<!-- This is for ensuring the background service still runs when/if the app is swiped away -->
|
||||
<activity
|
||||
android:name=".service.util.DummyActivity"
|
||||
android:allowTaskReparenting="true"
|
||||
android:alwaysRetainTaskState="false"
|
||||
android:clearTaskOnLaunch="true"
|
||||
android:enabled="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:finishOnTaskLaunch="true"
|
||||
android:noHistory="true"
|
||||
android:stateNotNeeded="true"
|
||||
android:theme="@android:style/Theme.Translucent" />
|
||||
<activity
|
||||
android:name=".vpn.VPNEnableActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/app_name" />
|
||||
<activity
|
||||
android:name=".ui.PromoAppsActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".settings.SettingsPreferences"
|
||||
android:label="@string/app_name" />
|
||||
<activity
|
||||
android:name=".ui.AppManager"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.AppCompat" />
|
||||
|
||||
<service
|
||||
android:name=".service.TorService"
|
||||
android:enabled="true"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||
android:stopWithTask="false" >
|
||||
android:stopWithTask="false"></service>
|
||||
<service
|
||||
android:name=".service.vpn.TorVpnService"
|
||||
android:enabled="true"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.net.VpnService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
|
||||
<service
|
||||
android:name=".service.vpn.TorVpnService"
|
||||
android:enabled="true"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE" >
|
||||
<intent-filter>
|
||||
<action android:name="android.net.VpnService"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver
|
||||
android:name=".service.StartTorReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="org.torproject.android.intent.action.START" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="org.torproject.android.intent.action.START" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".OnBootReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
|
||||
<category android:name="android.intent.category.HOME" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||
|
||||
<category android:name="android.intent.category.HOME" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MEDIA_MOUNTED" />
|
||||
|
||||
<category android:name="android.intent.category.HOME" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".OnBootReceiver"
|
||||
android:enabled="true" android:exported="true"
|
||||
<activity
|
||||
android:name=".ui.hiddenservices.HiddenServicesActivity"
|
||||
android:label="@string/title_activity_hidden_services"
|
||||
android:theme="@style/DefaultTheme">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".OrbotMainActivity" />
|
||||
</activity>
|
||||
|
||||
>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<category android:name="android.intent.category.HOME" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||
<category android:name="android.intent.category.HOME" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
|
||||
<category android:name="android.intent.category.HOME" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<provider
|
||||
android:name=".ui.hiddenservices.providers.HSContentProvider"
|
||||
android:authorities="org.torproject.android.ui.hiddenservices.providers"
|
||||
android:exported="false" />
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
android:authorities="org.torproject.android.ui.hiddenservices.storage"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/hidden_services_paths" />
|
||||
</provider>
|
||||
|
||||
<activity
|
||||
android:name=".ui.hiddenservices.ClientCookiesActivity"
|
||||
android:label="@string/client_cookies"
|
||||
android:theme="@style/DefaultTheme">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".OrbotMainActivity" />
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
android:name=".ui.hiddenservices.providers.CookieContentProvider"
|
||||
android:authorities="org.torproject.android.ui.hiddenservices.providers.cookie"
|
||||
android:exported="false" />
|
||||
</application>
|
||||
|
||||
</application>
|
||||
</manifest>
|
|
@ -4,6 +4,7 @@
|
|||
package org.torproject.android;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
|
@ -26,6 +27,11 @@ import org.torproject.android.ui.AppManager;
|
|||
import org.torproject.android.ui.ImageProgressView;
|
||||
import org.torproject.android.ui.PromoAppsActivity;
|
||||
import org.torproject.android.ui.Rotate3dAnimation;
|
||||
import org.torproject.android.ui.hiddenservices.ClientCookiesActivity;
|
||||
import org.torproject.android.ui.hiddenservices.HiddenServicesActivity;
|
||||
import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
|
||||
import org.torproject.android.ui.hiddenservices.permissions.PermissionManager;
|
||||
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
||||
import org.torproject.android.vpn.VPNEnableActivity;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
@ -34,6 +40,8 @@ import android.app.ActivityManager.RunningServiceInfo;
|
|||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
|
@ -43,6 +51,7 @@ import android.content.SharedPreferences.Editor;
|
|||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
@ -80,6 +89,8 @@ import android.widget.Toast;
|
|||
import com.google.zxing.integration.android.IntentIntegrator;
|
||||
import com.google.zxing.integration.android.IntentResult;
|
||||
|
||||
import static android.support.v4.content.FileProvider.getUriForFile;
|
||||
|
||||
public class OrbotMainActivity extends AppCompatActivity
|
||||
implements OrbotConstants, OnLongClickListener, OnTouchListener {
|
||||
|
||||
|
@ -114,8 +125,6 @@ public class OrbotMainActivity extends AppCompatActivity
|
|||
private final static int REQUEST_SETTINGS = 0x9874;
|
||||
private final static int REQUEST_VPN_APPS_SELECT = 8889;
|
||||
|
||||
private final static boolean mIsLollipop = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
|
||||
|
||||
// message types for mStatusUpdateHandler
|
||||
private final static int STATUS_UPDATE = 1;
|
||||
private static final int MESSAGE_TRAFFIC_COUNT = 2;
|
||||
|
@ -141,12 +150,36 @@ public class OrbotMainActivity extends AppCompatActivity
|
|||
return null;
|
||||
}
|
||||
|
||||
/** Called when the activity is first created. */
|
||||
private void migratePreferences() {
|
||||
String hsPortString = mPrefs.getString("pref_hs_ports", "");
|
||||
if (hsPortString.length() > 0) {
|
||||
StringTokenizer st = new StringTokenizer(hsPortString, ",");
|
||||
ContentResolver cr = getContentResolver();
|
||||
while (st.hasMoreTokens()) {
|
||||
int hsPort = Integer.parseInt(st.nextToken().split(" ")[0]);
|
||||
ContentValues fields = new ContentValues();
|
||||
fields.put("name", hsPort);
|
||||
fields.put("port", hsPort);
|
||||
fields.put("onion_port", hsPort);
|
||||
cr.insert(HSContentProvider.CONTENT_URI, fields);
|
||||
}
|
||||
|
||||
Editor pEdit = mPrefs.edit();
|
||||
pEdit.remove("pref_hs_ports");
|
||||
pEdit.commit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity is first created.
|
||||
*/
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mPrefs = TorServiceUtils.getSharedPrefs(getApplicationContext());
|
||||
|
||||
migratePreferences(); // Migrate old preferences
|
||||
|
||||
/* Create the widgets before registering for broadcasts to guarantee
|
||||
* that the widgets exist when the status updates try to update them */
|
||||
doLayout();
|
||||
|
@ -478,6 +511,10 @@ public class OrbotMainActivity extends AppCompatActivity
|
|||
}
|
||||
}
|
||||
|
||||
} else if (item.getItemId() == R.id.menu_hidden_services) {
|
||||
startActivity(new Intent(this, HiddenServicesActivity.class));
|
||||
} else if (item.getItemId() == R.id.menu_client_cookies) {
|
||||
startActivity(new Intent(this, ClientCookiesActivity.class));
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
|
@ -548,7 +585,7 @@ public class OrbotMainActivity extends AppCompatActivity
|
|||
Prefs.putUseVpn(enable);
|
||||
|
||||
if (enable) {
|
||||
if (mIsLollipop) //let the user choose the apps
|
||||
if (PermissionManager.isLollipopOrHigher()) //let the user choose the apps
|
||||
startActivityForResult(new Intent(OrbotMainActivity.this, AppManager.class), REQUEST_VPN_APPS_SELECT);
|
||||
else
|
||||
startActivity(new Intent(OrbotMainActivity.this, VPNEnableActivity.class));
|
||||
|
@ -556,184 +593,241 @@ public class OrbotMainActivity extends AppCompatActivity
|
|||
stopVpnService();
|
||||
}
|
||||
|
||||
private void enableHiddenServicePort (int hsPort) throws RemoteException, InterruptedException
|
||||
{
|
||||
private void enableHiddenServicePort(
|
||||
String hsName, final int hsPort, int hsRemotePort,
|
||||
final String backupToPackage, final Uri hsKeyPath,
|
||||
final Boolean authCookie
|
||||
) throws RemoteException, InterruptedException {
|
||||
|
||||
Editor pEdit = mPrefs.edit();
|
||||
String onionHostname = null;
|
||||
|
||||
String hsPortString = mPrefs.getString("pref_hs_ports", "");
|
||||
String onionHostname = mPrefs.getString("pref_hs_hostname","");
|
||||
if (hsName == null)
|
||||
hsName = "hs" + hsPort;
|
||||
|
||||
if (hsPortString.indexOf(hsPort+"")==-1)
|
||||
{
|
||||
if (hsPortString.length() > 0 && hsPortString.indexOf(hsPort+"")==-1)
|
||||
hsPortString += ',' + hsPort;
|
||||
else
|
||||
hsPortString = hsPort + "";
|
||||
if (hsRemotePort == -1)
|
||||
hsRemotePort = hsPort;
|
||||
|
||||
pEdit.putString("pref_hs_ports", hsPortString);
|
||||
pEdit.putBoolean("pref_hs_enable", true);
|
||||
ContentValues fields = new ContentValues();
|
||||
fields.put(HSContentProvider.HiddenService.NAME, hsName);
|
||||
fields.put(HSContentProvider.HiddenService.PORT, hsPort);
|
||||
fields.put(HSContentProvider.HiddenService.ONION_PORT, hsRemotePort);
|
||||
fields.put(HSContentProvider.HiddenService.AUTH_COOKIE, authCookie);
|
||||
|
||||
pEdit.commit();
|
||||
}
|
||||
ContentResolver cr = getContentResolver();
|
||||
|
||||
if (onionHostname == null || onionHostname.length() == 0)
|
||||
{
|
||||
requestTorRereadConfig();
|
||||
Cursor row = cr.query(
|
||||
HSContentProvider.CONTENT_URI,
|
||||
HSContentProvider.PROJECTION,
|
||||
HSContentProvider.HiddenService.ONION_PORT + "=" + hsPort,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
new Thread () {
|
||||
if (row == null || row.getCount() < 1) {
|
||||
cr.insert(HSContentProvider.CONTENT_URI, fields);
|
||||
} else {
|
||||
onionHostname = row.getString(row.getColumnIndex(HSContentProvider.HiddenService.DOMAIN));
|
||||
row.close();
|
||||
}
|
||||
|
||||
public void run ()
|
||||
{
|
||||
String onionHostname = mPrefs.getString("pref_hs_hostname","");
|
||||
if (onionHostname == null || onionHostname.length() < 1) {
|
||||
|
||||
while (onionHostname.length() == 0)
|
||||
{
|
||||
//we need to stop and start Tor
|
||||
try {
|
||||
Thread.sleep(3000); //wait three seconds
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (hsKeyPath != null) {
|
||||
BackupUtils hsutils = new BackupUtils(getApplicationContext());
|
||||
hsutils.restoreKeyBackup(hsPort, hsKeyPath);
|
||||
}
|
||||
|
||||
onionHostname = mPrefs.getString("pref_hs_hostname","");
|
||||
}
|
||||
if (torStatus.equals(TorServiceConstants.STATUS_OFF)) {
|
||||
startTor();
|
||||
} else {
|
||||
stopTor();
|
||||
Toast.makeText(
|
||||
this, R.string.start_tor_again_for_finish_the_process, Toast.LENGTH_LONG
|
||||
).show();
|
||||
}
|
||||
|
||||
Intent nResult = new Intent();
|
||||
nResult.putExtra("hs_host", onionHostname);
|
||||
setResult(RESULT_OK, nResult);
|
||||
finish();
|
||||
}
|
||||
}.start();
|
||||
new Thread() {
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Intent nResult = new Intent();
|
||||
nResult.putExtra("hs_host", onionHostname);
|
||||
setResult(RESULT_OK, nResult);
|
||||
finish();
|
||||
}
|
||||
public void run() {
|
||||
String hostname = null;
|
||||
Intent nResult = new Intent();
|
||||
|
||||
}
|
||||
while (hostname == null) {
|
||||
try {
|
||||
Thread.sleep(3000); //wait three seconds
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
private synchronized void handleIntents ()
|
||||
{
|
||||
if (getIntent() == null)
|
||||
return;
|
||||
Cursor onion = getContentResolver().query(
|
||||
HSContentProvider.CONTENT_URI,
|
||||
HSContentProvider.PROJECTION,
|
||||
HSContentProvider.HiddenService.ONION_PORT + "=" + hsPort,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// Get intent, action and MIME type
|
||||
Intent intent = getIntent();
|
||||
String action = intent.getAction();
|
||||
Log.d(TAG, "handleIntents " + action);
|
||||
if (onion != null && onion.getCount() > 0) {
|
||||
onion.moveToNext();
|
||||
hostname = onion.getString(onion.getColumnIndex(HSContentProvider.HiddenService.DOMAIN));
|
||||
|
||||
//String type = intent.getType();
|
||||
if(hostname == null || hostname.length() < 1)
|
||||
continue;
|
||||
|
||||
if (action == null)
|
||||
return;
|
||||
nResult.putExtra("hs_host", hostname);
|
||||
|
||||
if (action.equals(INTENT_ACTION_REQUEST_HIDDEN_SERVICE))
|
||||
{
|
||||
final int hiddenServicePortRequest = getIntent().getIntExtra("hs_port", -1);
|
||||
if (authCookie) {
|
||||
nResult.putExtra(
|
||||
"hs_auth_cookie",
|
||||
onion.getString(onion.getColumnIndex(HSContentProvider.HiddenService.AUTH_COOKIE_VALUE))
|
||||
);
|
||||
}
|
||||
|
||||
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
|
||||
if (backupToPackage != null && backupToPackage.length() > 0) {
|
||||
String servicePath = getFilesDir() + "/" + TorServiceConstants.HIDDEN_SERVICES_DIR + "/hs" + hsPort;
|
||||
File hidden_service_key = new File(servicePath, "private_key");
|
||||
Context context = getApplicationContext();
|
||||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
switch (which){
|
||||
case DialogInterface.BUTTON_POSITIVE:
|
||||
Uri contentUri = getUriForFile(
|
||||
context,
|
||||
"org.torproject.android.ui.hiddenservices.storage",
|
||||
hidden_service_key
|
||||
);
|
||||
|
||||
try {
|
||||
enableHiddenServicePort (hiddenServicePortRequest);
|
||||
context.grantUriPermission(backupToPackage, contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
nResult.setData(contentUri);
|
||||
nResult.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}
|
||||
|
||||
} catch (RemoteException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
onion.close();
|
||||
setResult(RESULT_OK, nResult);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
|
||||
} else {
|
||||
Intent nResult = new Intent();
|
||||
nResult.putExtra("hs_host", onionHostname);
|
||||
setResult(RESULT_OK, nResult);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
private synchronized void handleIntents() {
|
||||
if (getIntent() == null)
|
||||
return;
|
||||
|
||||
case DialogInterface.BUTTON_NEGATIVE:
|
||||
//No button clicked
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
// Get intent, action and MIME type
|
||||
Intent intent = getIntent();
|
||||
String action = intent.getAction();
|
||||
Log.d(TAG, "handleIntents " + action);
|
||||
|
||||
String requestMsg = getString(R.string.hidden_service_request, hiddenServicePortRequest);
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setMessage(requestMsg).setPositiveButton("Allow", dialogClickListener)
|
||||
.setNegativeButton("Deny", dialogClickListener).show();
|
||||
//String type = intent.getType();
|
||||
|
||||
return; //don't null the setIntent() as we need it later
|
||||
}
|
||||
else if (action.equals(INTENT_ACTION_REQUEST_START_TOR))
|
||||
{
|
||||
autoStartFromIntent = true;
|
||||
if (action == null)
|
||||
return;
|
||||
|
||||
startTor();
|
||||
switch (action) {
|
||||
case INTENT_ACTION_REQUEST_HIDDEN_SERVICE:
|
||||
final int hiddenServicePort = intent.getIntExtra("hs_port", -1);
|
||||
final int hiddenServiceRemotePort = intent.getIntExtra("hs_onion_port", -1);
|
||||
final String hiddenServiceName = intent.getStringExtra("hs_name");
|
||||
final String backupToPackage = intent.getStringExtra("hs_backup_to_package");
|
||||
final Boolean authCookie = intent.getBooleanExtra("hs_auth_cookie", false);
|
||||
final Uri mKeyUri = intent.getData();
|
||||
|
||||
//never allow backgrounds start from this type of intent start
|
||||
//app devs who want background starts, can use the service intents
|
||||
/**
|
||||
if (Prefs.allowBackgroundStarts())
|
||||
{
|
||||
Intent resultIntent;
|
||||
if (lastStatusIntent == null) {
|
||||
resultIntent = new Intent(intent);
|
||||
} else {
|
||||
resultIntent = lastStatusIntent;
|
||||
}
|
||||
resultIntent.putExtra(TorServiceConstants.EXTRA_STATUS, torStatus);
|
||||
setResult(RESULT_OK, resultIntent);
|
||||
finish();
|
||||
}*/
|
||||
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
|
||||
|
||||
}
|
||||
else if (action.equals(Intent.ACTION_VIEW))
|
||||
{
|
||||
String urlString = intent.getDataString();
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
switch (which) {
|
||||
case DialogInterface.BUTTON_POSITIVE:
|
||||
try {
|
||||
enableHiddenServicePort(
|
||||
hiddenServiceName, hiddenServicePort,
|
||||
hiddenServiceRemotePort, backupToPackage,
|
||||
mKeyUri, authCookie
|
||||
);
|
||||
} catch (RemoteException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (urlString != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (urlString.toLowerCase().startsWith("bridge://"))
|
||||
String requestMsg = getString(R.string.hidden_service_request, hiddenServicePort);
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setMessage(requestMsg).setPositiveButton("Allow", dialogClickListener)
|
||||
.setNegativeButton("Deny", dialogClickListener).show();
|
||||
|
||||
{
|
||||
String newBridgeValue = urlString.substring(9); //remove the bridge protocol piece
|
||||
newBridgeValue = URLDecoder.decode(newBridgeValue); //decode the value here
|
||||
return; //don't null the setIntent() as we need it later
|
||||
|
||||
showAlert(getString(R.string.bridges_updated),getString(R.string.restart_orbot_to_use_this_bridge_) + newBridgeValue,false);
|
||||
case INTENT_ACTION_REQUEST_START_TOR:
|
||||
autoStartFromIntent = true;
|
||||
|
||||
setNewBridges(newBridgeValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
startTor();
|
||||
|
||||
updateStatus(null);
|
||||
//never allow backgrounds start from this type of intent start
|
||||
//app devs who want background starts, can use the service intents
|
||||
/**
|
||||
if (Prefs.allowBackgroundStarts())
|
||||
{
|
||||
Intent resultIntent;
|
||||
if (lastStatusIntent == null) {
|
||||
resultIntent = new Intent(intent);
|
||||
} else {
|
||||
resultIntent = lastStatusIntent;
|
||||
}
|
||||
resultIntent.putExtra(TorServiceConstants.EXTRA_STATUS, torStatus);
|
||||
setResult(RESULT_OK, resultIntent);
|
||||
finish();
|
||||
}*/
|
||||
|
||||
setIntent(null);
|
||||
break;
|
||||
case Intent.ACTION_VIEW:
|
||||
String urlString = intent.getDataString();
|
||||
|
||||
if (urlString != null) {
|
||||
|
||||
}
|
||||
if (urlString.toLowerCase().startsWith("bridge://"))
|
||||
|
||||
private void setNewBridges (String newBridgeValue)
|
||||
{
|
||||
{
|
||||
String newBridgeValue = urlString.substring(9); //remove the bridge protocol piece
|
||||
newBridgeValue = URLDecoder.decode(newBridgeValue); //decode the value here
|
||||
|
||||
Prefs.setBridgesList(newBridgeValue); //set the string to a preference
|
||||
Prefs.putBridgesEnabled(true);
|
||||
showAlert(getString(R.string.bridges_updated), getString(R.string.restart_orbot_to_use_this_bridge_) + newBridgeValue, false);
|
||||
|
||||
setResult(RESULT_OK);
|
||||
setNewBridges(newBridgeValue);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
mBtnBridges.setChecked(true);
|
||||
updateStatus(null);
|
||||
|
||||
enableBridges(true);
|
||||
}
|
||||
setIntent(null);
|
||||
|
||||
}
|
||||
|
||||
private void setNewBridges(String newBridgeValue) {
|
||||
|
||||
Prefs.setBridgesList(newBridgeValue); //set the string to a preference
|
||||
Prefs.putBridgesEnabled(true);
|
||||
|
||||
setResult(RESULT_OK);
|
||||
|
||||
mBtnBridges.setChecked(true);
|
||||
|
||||
enableBridges(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Launch the system activity for Uri viewing with the provided url
|
||||
|
@ -1194,10 +1288,6 @@ public class OrbotMainActivity extends AppCompatActivity
|
|||
{
|
||||
autoStartFromIntent = false;
|
||||
Intent resultIntent = lastStatusIntent;
|
||||
|
||||
if (resultIntent == null)
|
||||
resultIntent = new Intent(TorServiceConstants.ACTION_START);
|
||||
|
||||
resultIntent.putExtra(TorServiceConstants.EXTRA_STATUS, torStatus);
|
||||
setResult(RESULT_OK, resultIntent);
|
||||
finish();
|
||||
|
|
|
@ -37,9 +37,6 @@ public class SettingsPreferences
|
|||
private Preference prefTransProxyFlush = null;
|
||||
|
||||
private Preference prefTransProxyApps = null;
|
||||
private CheckBoxPreference prefHiddenServices = null;
|
||||
private EditTextPreference prefHiddenServicesPorts;
|
||||
private EditTextPreference prefHiddenServicesHostname;
|
||||
private CheckBoxPreference prefRequestRoot = null;
|
||||
private ListPreference prefLocale = null;
|
||||
|
||||
|
@ -104,10 +101,6 @@ public class SettingsPreferences
|
|||
prefTransProxyApps.setOnPreferenceClickListener(this);
|
||||
prefCBTransProxy.setOnPreferenceClickListener(this);
|
||||
prefcBTransProxyAll.setOnPreferenceClickListener(this);
|
||||
prefHiddenServices = (CheckBoxPreference) findPreference("pref_hs_enable");
|
||||
prefHiddenServices.setOnPreferenceClickListener(this);
|
||||
prefHiddenServicesHostname = (EditTextPreference) findPreference("pref_hs_hostname");
|
||||
|
||||
|
||||
prefCBTransProxy.setEnabled(prefRequestRoot.isChecked());
|
||||
prefcBTransProxyAll.setEnabled(prefCBTransProxy.isChecked());
|
||||
|
@ -116,10 +109,6 @@ public class SettingsPreferences
|
|||
if (prefCBTransProxy.isChecked())
|
||||
prefTransProxyApps.setEnabled((!prefcBTransProxyAll.isChecked()));
|
||||
|
||||
prefHiddenServicesPorts = (EditTextPreference) findPreference("pref_hs_ports");
|
||||
prefHiddenServicesHostname.setEnabled(prefHiddenServices.isChecked());
|
||||
prefHiddenServicesPorts.setEnabled(prefHiddenServices.isChecked());
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
{
|
||||
prefTransProxyApps.setEnabled(true);
|
||||
|
@ -153,11 +142,6 @@ public class SettingsPreferences
|
|||
{
|
||||
startActivity(new Intent(this, AppManager.class));
|
||||
|
||||
}
|
||||
else if (preference == prefHiddenServices)
|
||||
{
|
||||
prefHiddenServicesPorts.setEnabled(prefHiddenServices.isChecked());
|
||||
prefHiddenServicesHostname.setEnabled(prefHiddenServices.isChecked());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
package org.torproject.android.ui.hiddenservices;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.zxing.integration.android.IntentIntegrator;
|
||||
import com.google.zxing.integration.android.IntentResult;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.torproject.android.R;
|
||||
import org.torproject.android.ui.hiddenservices.adapters.ClientCookiesAdapter;
|
||||
import org.torproject.android.ui.hiddenservices.dialogs.AddCookieDialog;
|
||||
import org.torproject.android.ui.hiddenservices.dialogs.CookieActionsDialog;
|
||||
import org.torproject.android.ui.hiddenservices.dialogs.SelectCookieBackupDialog;
|
||||
import org.torproject.android.ui.hiddenservices.permissions.PermissionManager;
|
||||
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
||||
|
||||
public class ClientCookiesActivity extends AppCompatActivity {
|
||||
public final int WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTIONBAR = 3;
|
||||
|
||||
private ContentResolver mResolver;
|
||||
private ClientCookiesAdapter mAdapter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.layout_activity_client_cookies);
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
mResolver = getContentResolver();
|
||||
|
||||
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
|
||||
fab.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
AddCookieDialog dialog = new AddCookieDialog();
|
||||
dialog.show(getSupportFragmentManager(), "AddCookieDialog");
|
||||
}
|
||||
});
|
||||
|
||||
mAdapter = new ClientCookiesAdapter(
|
||||
this,
|
||||
mResolver.query(CookieContentProvider.CONTENT_URI, CookieContentProvider.PROJECTION, null, null, null)
|
||||
, 0);
|
||||
|
||||
mResolver.registerContentObserver(
|
||||
CookieContentProvider.CONTENT_URI, true, new HSObserver(new Handler())
|
||||
);
|
||||
|
||||
ListView cookies = (ListView) findViewById(R.id.clien_cookies_list);
|
||||
cookies.setAdapter(mAdapter);
|
||||
|
||||
cookies.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Cursor item = (Cursor) parent.getItemAtPosition(position);
|
||||
|
||||
Bundle arguments = new Bundle();
|
||||
arguments.putInt(
|
||||
"_id", item.getInt(item.getColumnIndex(CookieContentProvider.ClientCookie._ID))
|
||||
);
|
||||
|
||||
arguments.putString(
|
||||
"domain", item.getString(item.getColumnIndex(CookieContentProvider.ClientCookie.DOMAIN))
|
||||
);
|
||||
|
||||
arguments.putString(
|
||||
"auth_cookie_value", item.getString(item.getColumnIndex(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE))
|
||||
);
|
||||
|
||||
arguments.putInt(
|
||||
"enabled", item.getInt(item.getColumnIndex(CookieContentProvider.ClientCookie.ENABLED))
|
||||
);
|
||||
|
||||
CookieActionsDialog dialog = new CookieActionsDialog();
|
||||
dialog.setArguments(arguments);
|
||||
dialog.show(getSupportFragmentManager(), "CookieActionsDialog");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.cookie_menu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
|
||||
if (id == R.id.cookie_restore_backup) {
|
||||
if (PermissionManager.isLollipopOrHigher()
|
||||
&& !PermissionManager.hasExternalWritePermission(this)) {
|
||||
PermissionManager.requestExternalWritePermissions(this, WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTIONBAR);
|
||||
return true;
|
||||
}
|
||||
|
||||
SelectCookieBackupDialog dialog = new SelectCookieBackupDialog();
|
||||
dialog.show(getSupportFragmentManager(), "SelectCookieBackupDialog");
|
||||
|
||||
} else if (id == R.id.cookie_from_qr) {
|
||||
IntentIntegrator integrator = new IntentIntegrator(ClientCookiesActivity.this);
|
||||
integrator.initiateScan();
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode,
|
||||
String permissions[], int[] grantResults) {
|
||||
if (grantResults.length < 1
|
||||
|| grantResults[0] != PackageManager.PERMISSION_GRANTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (requestCode) {
|
||||
case WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTIONBAR: {
|
||||
SelectCookieBackupDialog dialog = new SelectCookieBackupDialog();
|
||||
dialog.show(getSupportFragmentManager(), "SelectCookieBackupDialog");
|
||||
break;
|
||||
}
|
||||
case CookieActionsDialog.WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTION_DIALOG: {
|
||||
Toast.makeText(this, R.string.click_again_for_backup, Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int request, int response, Intent data) {
|
||||
super.onActivityResult(request, response, data);
|
||||
|
||||
IntentResult scanResult = IntentIntegrator.parseActivityResult(request, response, data);
|
||||
|
||||
if (scanResult == null) return;
|
||||
|
||||
String results = scanResult.getContents();
|
||||
|
||||
if (results == null || results.length() < 1) return;
|
||||
|
||||
try {
|
||||
JSONObject savedValues = new JSONObject(results);
|
||||
ContentValues fields = new ContentValues();
|
||||
|
||||
fields.put(
|
||||
CookieContentProvider.ClientCookie.DOMAIN,
|
||||
savedValues.getString(CookieContentProvider.ClientCookie.DOMAIN)
|
||||
);
|
||||
|
||||
fields.put(
|
||||
CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE,
|
||||
savedValues.getString(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE)
|
||||
);
|
||||
|
||||
mResolver.insert(CookieContentProvider.CONTENT_URI, fields);
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(this, R.string.error, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
class HSObserver extends ContentObserver {
|
||||
HSObserver(Handler handler) {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
mAdapter.changeCursor(mResolver.query(
|
||||
CookieContentProvider.CONTENT_URI, CookieContentProvider.PROJECTION, null, null, null
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
package org.torproject.android.ui.hiddenservices;
|
||||
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.torproject.android.R;
|
||||
import org.torproject.android.ui.hiddenservices.adapters.OnionListAdapter;
|
||||
import org.torproject.android.ui.hiddenservices.dialogs.HSActionsDialog;
|
||||
import org.torproject.android.ui.hiddenservices.dialogs.HSDataDialog;
|
||||
import org.torproject.android.ui.hiddenservices.dialogs.SelectHSBackupDialog;
|
||||
import org.torproject.android.ui.hiddenservices.permissions.PermissionManager;
|
||||
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
||||
|
||||
public class HiddenServicesActivity extends AppCompatActivity {
|
||||
public final int WRITE_EXTERNAL_STORAGE_FROM_ACTIONBAR = 1;
|
||||
private ContentResolver mResolver;
|
||||
private OnionListAdapter mAdapter;
|
||||
private FloatingActionButton fab;
|
||||
|
||||
private String mWhere = HSContentProvider.HiddenService.CREATED_BY_USER + "=1";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.layout_hs_list_view);
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
mResolver = getContentResolver();
|
||||
|
||||
fab = (FloatingActionButton) findViewById(R.id.fab);
|
||||
fab.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
HSDataDialog dialog = new HSDataDialog();
|
||||
dialog.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 = (ListView) findViewById(R.id.onion_list);
|
||||
onion_list.setAdapter(mAdapter);
|
||||
|
||||
onion_list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Cursor item = (Cursor) parent.getItemAtPosition(position);
|
||||
|
||||
Bundle arguments = new Bundle();
|
||||
arguments.putInt(
|
||||
"_id", item.getInt(item.getColumnIndex(HSContentProvider.HiddenService._ID))
|
||||
);
|
||||
|
||||
arguments.putString(
|
||||
"port", item.getString(item.getColumnIndex(HSContentProvider.HiddenService.PORT))
|
||||
);
|
||||
|
||||
arguments.putString(
|
||||
"onion", item.getString(item.getColumnIndex(HSContentProvider.HiddenService.DOMAIN))
|
||||
);
|
||||
|
||||
arguments.putInt(
|
||||
"auth_cookie", item.getInt(item.getColumnIndex(HSContentProvider.HiddenService.AUTH_COOKIE))
|
||||
);
|
||||
|
||||
arguments.putString(
|
||||
"auth_cookie_value", item.getString(item.getColumnIndex(HSContentProvider.HiddenService.AUTH_COOKIE_VALUE))
|
||||
);
|
||||
|
||||
HSActionsDialog dialog = new HSActionsDialog();
|
||||
dialog.setArguments(arguments);
|
||||
dialog.show(getSupportFragmentManager(), "HSActionsDialog");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.hs_menu, menu);
|
||||
|
||||
MenuItem item = menu.findItem(R.id.hs_type);
|
||||
Spinner spinner = (Spinner) MenuItemCompat.getActionView(item);
|
||||
|
||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
|
||||
this, R.array.array_hs_types, android.R.layout.simple_spinner_item);
|
||||
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
|
||||
spinner.setAdapter(adapter);
|
||||
|
||||
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> arg0, View v, int pos, long id) {
|
||||
if (pos == 0) {
|
||||
mWhere = HSContentProvider.HiddenService.CREATED_BY_USER + "=1";
|
||||
fab.show();
|
||||
} else {
|
||||
mWhere = HSContentProvider.HiddenService.CREATED_BY_USER + "=0";
|
||||
fab.hide();
|
||||
}
|
||||
|
||||
mAdapter.changeCursor(mResolver.query(
|
||||
HSContentProvider.CONTENT_URI, HSContentProvider.PROJECTION, mWhere, null, null
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> arg0) {
|
||||
// Do nothing
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
|
||||
if (id == R.id.menu_restore_backup) {
|
||||
if (PermissionManager.isLollipopOrHigher()
|
||||
&& !PermissionManager.hasExternalWritePermission(this)) {
|
||||
PermissionManager.requestExternalWritePermissions(this, WRITE_EXTERNAL_STORAGE_FROM_ACTIONBAR);
|
||||
return true;
|
||||
}
|
||||
|
||||
SelectHSBackupDialog dialog = new SelectHSBackupDialog();
|
||||
dialog.show(getSupportFragmentManager(), "SelectHSBackupDialog");
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode,
|
||||
String permissions[], int[] grantResults) {
|
||||
if (grantResults.length < 1
|
||||
|| grantResults[0] != PackageManager.PERMISSION_GRANTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (requestCode) {
|
||||
case WRITE_EXTERNAL_STORAGE_FROM_ACTIONBAR: {
|
||||
SelectHSBackupDialog dialog = new SelectHSBackupDialog();
|
||||
dialog.show(getSupportFragmentManager(), "SelectHSBackupDialog");
|
||||
break;
|
||||
}
|
||||
case HSActionsDialog.WRITE_EXTERNAL_STORAGE_FROM_ACTION_DIALOG: {
|
||||
Toast.makeText(this, R.string.click_again_for_backup, Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HSObserver extends ContentObserver {
|
||||
HSObserver(Handler handler) {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
mAdapter.changeCursor(mResolver.query(
|
||||
HSContentProvider.CONTENT_URI, HSContentProvider.PROJECTION, mWhere, null, null
|
||||
));
|
||||
|
||||
if (PermissionManager.isLollipopOrHigher()) {
|
||||
Cursor active = mResolver.query(
|
||||
HSContentProvider.CONTENT_URI, HSContentProvider.PROJECTION, HSContentProvider.HiddenService.ENABLED + "=1", null, null
|
||||
);
|
||||
|
||||
if (active == null) return;
|
||||
|
||||
if (active.getCount() > 0) // Call only if there running services
|
||||
PermissionManager.requestBatteryPermmssions(HiddenServicesActivity.this, getApplicationContext());
|
||||
else // Drop whe not needed
|
||||
PermissionManager.requestDropBatteryPermmssions(HiddenServicesActivity.this, getApplicationContext());
|
||||
|
||||
active.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package org.torproject.android.ui.hiddenservices.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.torproject.android.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public class BackupAdapter extends ArrayAdapter<File> {
|
||||
private int mResource;
|
||||
|
||||
public BackupAdapter(Context context, int resource) {
|
||||
super(context, resource);
|
||||
mResource = resource;
|
||||
}
|
||||
|
||||
public BackupAdapter(Context context, int resource, List<File> zips) {
|
||||
super(context, resource, zips);
|
||||
mResource = resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
|
||||
View v = convertView;
|
||||
|
||||
if (v == null) {
|
||||
LayoutInflater vi;
|
||||
vi = LayoutInflater.from(getContext());
|
||||
v = vi.inflate(mResource, null);
|
||||
}
|
||||
|
||||
File p = getItem(position);
|
||||
|
||||
if (p != null) {
|
||||
TextView name = (TextView) v.findViewById(R.id.backup_name);
|
||||
|
||||
if (name != null)
|
||||
name.setText(p.getName());
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package org.torproject.android.ui.hiddenservices.adapters;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.support.v4.widget.CursorAdapter;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.torproject.android.R;
|
||||
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
||||
|
||||
public class ClientCookiesAdapter extends CursorAdapter {
|
||||
private LayoutInflater cursorInflater;
|
||||
|
||||
public ClientCookiesAdapter(Context context, Cursor c, int flags) {
|
||||
super(context, c, flags);
|
||||
|
||||
cursorInflater = (LayoutInflater) context.getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
final Context mContext = context;
|
||||
int id = cursor.getInt(cursor.getColumnIndex(CookieContentProvider.ClientCookie._ID));
|
||||
final String where = CookieContentProvider.ClientCookie._ID + "=" + id;
|
||||
|
||||
TextView domain = (TextView) view.findViewById(R.id.cookie_onion);
|
||||
domain.setText(cursor.getString(cursor.getColumnIndex(CookieContentProvider.ClientCookie.DOMAIN)));
|
||||
|
||||
Switch enabled = (Switch) view.findViewById(R.id.cookie_switch);
|
||||
enabled.setChecked(
|
||||
cursor.getInt(cursor.getColumnIndex(CookieContentProvider.ClientCookie.ENABLED)) == 1
|
||||
);
|
||||
|
||||
enabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
ContentResolver resolver = mContext.getContentResolver();
|
||||
ContentValues fields = new ContentValues();
|
||||
fields.put(CookieContentProvider.ClientCookie.ENABLED, isChecked);
|
||||
resolver.update(
|
||||
CookieContentProvider.CONTENT_URI, fields, where, null
|
||||
);
|
||||
|
||||
Toast.makeText(
|
||||
mContext, R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG
|
||||
).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
return cursorInflater.inflate(R.layout.layout_client_cookie_list_item, parent, false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package org.torproject.android.ui.hiddenservices.adapters;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.support.v4.widget.CursorAdapter;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.torproject.android.R;
|
||||
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
||||
|
||||
public class OnionListAdapter extends CursorAdapter {
|
||||
private LayoutInflater cursorInflater;
|
||||
|
||||
public OnionListAdapter(Context context, Cursor c, int flags) {
|
||||
super(context, c, flags);
|
||||
|
||||
cursorInflater = (LayoutInflater) context.getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
final Context mContext = context;
|
||||
int id = cursor.getInt(cursor.getColumnIndex(HSContentProvider.HiddenService._ID));
|
||||
final String where = HSContentProvider.HiddenService._ID + "=" + id;
|
||||
|
||||
TextView port = (TextView) view.findViewById(R.id.hs_port);
|
||||
port.setText(cursor.getString(cursor.getColumnIndex(HSContentProvider.HiddenService.PORT)));
|
||||
TextView name = (TextView) view.findViewById(R.id.hs_name);
|
||||
name.setText(cursor.getString(cursor.getColumnIndex(HSContentProvider.HiddenService.NAME)));
|
||||
TextView domain = (TextView) view.findViewById(R.id.hs_onion);
|
||||
domain.setText(cursor.getString(cursor.getColumnIndex(HSContentProvider.HiddenService.DOMAIN)));
|
||||
|
||||
Switch enabled = (Switch) view.findViewById(R.id.hs_switch);
|
||||
enabled.setChecked(
|
||||
cursor.getInt(cursor.getColumnIndex(HSContentProvider.HiddenService.ENABLED)) == 1
|
||||
);
|
||||
|
||||
enabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
ContentResolver resolver = mContext.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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
return cursorInflater.inflate(R.layout.layout_hs_list_item, parent, false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,336 @@
|
|||
package org.torproject.android.ui.hiddenservices.backup;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.torproject.android.R;
|
||||
import org.torproject.android.service.TorServiceConstants;
|
||||
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
||||
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
||||
import org.torproject.android.ui.hiddenservices.storage.ExternalStorage;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class BackupUtils {
|
||||
private final String configFileName = "config.json";
|
||||
private Context mContext;
|
||||
private ContentResolver mResolver;
|
||||
|
||||
public BackupUtils(Context context) {
|
||||
mContext = context;
|
||||
mResolver = mContext.getContentResolver();
|
||||
}
|
||||
|
||||
public String createZipBackup(Integer port) {
|
||||
File mHSBasePath = new File(
|
||||
mContext.getFilesDir().getAbsolutePath(),
|
||||
TorServiceConstants.HIDDEN_SERVICES_DIR
|
||||
);
|
||||
|
||||
String configFilePath = mHSBasePath + "/hs" + port + "/" + configFileName;
|
||||
String hostnameFilePath = mHSBasePath + "/hs" + port + "/hostname";
|
||||
String keyFilePath = mHSBasePath + "/hs" + port + "/private_key";
|
||||
|
||||
File storage_path = ExternalStorage.getOrCreateBackupDir();
|
||||
|
||||
if (storage_path == null)
|
||||
return null;
|
||||
|
||||
Cursor portData = mResolver.query(
|
||||
HSContentProvider.CONTENT_URI,
|
||||
HSContentProvider.PROJECTION,
|
||||
HSContentProvider.HiddenService.PORT + "=" + port,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
JSONObject config = new JSONObject();
|
||||
try {
|
||||
if (portData == null || portData.getCount() != 1)
|
||||
return null;
|
||||
|
||||
portData.moveToNext();
|
||||
|
||||
config.put(
|
||||
HSContentProvider.HiddenService.NAME,
|
||||
portData.getString(portData.getColumnIndex(HSContentProvider.HiddenService.NAME))
|
||||
);
|
||||
|
||||
config.put(
|
||||
HSContentProvider.HiddenService.PORT,
|
||||
portData.getInt(portData.getColumnIndex(HSContentProvider.HiddenService.PORT))
|
||||
);
|
||||
|
||||
config.put(
|
||||
HSContentProvider.HiddenService.ONION_PORT,
|
||||
portData.getInt(portData.getColumnIndex(HSContentProvider.HiddenService.ONION_PORT))
|
||||
);
|
||||
|
||||
config.put(
|
||||
HSContentProvider.HiddenService.DOMAIN,
|
||||
portData.getString(portData.getColumnIndex(HSContentProvider.HiddenService.DOMAIN))
|
||||
);
|
||||
|
||||
config.put(
|
||||
HSContentProvider.HiddenService.AUTH_COOKIE,
|
||||
portData.getInt(portData.getColumnIndex(HSContentProvider.HiddenService.AUTH_COOKIE))
|
||||
);
|
||||
|
||||
config.put(
|
||||
HSContentProvider.HiddenService.AUTH_COOKIE_VALUE,
|
||||
portData.getString(portData.getColumnIndex(HSContentProvider.HiddenService.AUTH_COOKIE_VALUE))
|
||||
);
|
||||
|
||||
config.put(
|
||||
HSContentProvider.HiddenService.CREATED_BY_USER,
|
||||
portData.getInt(portData.getColumnIndex(HSContentProvider.HiddenService.CREATED_BY_USER))
|
||||
);
|
||||
|
||||
config.put(
|
||||
HSContentProvider.HiddenService.ENABLED,
|
||||
portData.getInt(portData.getColumnIndex(HSContentProvider.HiddenService.ENABLED))
|
||||
);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} catch (NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
portData.close();
|
||||
|
||||
try {
|
||||
FileWriter file = new FileWriter(configFilePath);
|
||||
file.write(config.toString());
|
||||
file.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
String zip_path = storage_path.getAbsolutePath() + "/hs" + port + ".zip";
|
||||
String files[] = {hostnameFilePath, keyFilePath, configFilePath};
|
||||
|
||||
ZipIt zip = new ZipIt(files, zip_path);
|
||||
|
||||
if (!zip.zip())
|
||||
return null;
|
||||
|
||||
return zip_path;
|
||||
}
|
||||
|
||||
public void restoreZipBackup(File backup) {
|
||||
|
||||
File mHSBasePath = new File(
|
||||
mContext.getFilesDir().getAbsolutePath(),
|
||||
TorServiceConstants.HIDDEN_SERVICES_DIR
|
||||
);
|
||||
|
||||
int port;
|
||||
Cursor service;
|
||||
String backupName = backup.getName();
|
||||
String hsDir = backupName.substring(0, backupName.lastIndexOf('.'));
|
||||
String configFilePath = mHSBasePath + "/" + hsDir + "/" + configFileName;
|
||||
String jString = null;
|
||||
|
||||
File hsPath = new File(mHSBasePath.getAbsolutePath(), hsDir);
|
||||
if (!hsPath.isDirectory())
|
||||
hsPath.mkdirs();
|
||||
|
||||
ZipIt zip = new ZipIt(null, backup.getAbsolutePath());
|
||||
zip.unzip(hsPath.getAbsolutePath());
|
||||
|
||||
File config = new File(configFilePath);
|
||||
FileInputStream stream;
|
||||
|
||||
try {
|
||||
stream = new FileInputStream(config);
|
||||
FileChannel fc = stream.getChannel();
|
||||
MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
|
||||
jString = Charset.defaultCharset().decode(bb).toString();
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (jString == null)
|
||||
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
||||
|
||||
try {
|
||||
JSONObject savedValues = new JSONObject(jString);
|
||||
ContentValues fields = new ContentValues();
|
||||
|
||||
fields.put(
|
||||
HSContentProvider.HiddenService.NAME,
|
||||
savedValues.getString(HSContentProvider.HiddenService.NAME)
|
||||
);
|
||||
|
||||
fields.put(
|
||||
HSContentProvider.HiddenService.ONION_PORT,
|
||||
savedValues.getInt(HSContentProvider.HiddenService.ONION_PORT)
|
||||
);
|
||||
|
||||
fields.put(
|
||||
HSContentProvider.HiddenService.DOMAIN,
|
||||
savedValues.getString(HSContentProvider.HiddenService.DOMAIN)
|
||||
);
|
||||
|
||||
fields.put(
|
||||
HSContentProvider.HiddenService.AUTH_COOKIE,
|
||||
savedValues.getInt(HSContentProvider.HiddenService.AUTH_COOKIE)
|
||||
);
|
||||
|
||||
fields.put(
|
||||
HSContentProvider.HiddenService.CREATED_BY_USER,
|
||||
savedValues.getInt(HSContentProvider.HiddenService.CREATED_BY_USER)
|
||||
);
|
||||
|
||||
fields.put(
|
||||
HSContentProvider.HiddenService.ENABLED,
|
||||
savedValues.getInt(HSContentProvider.HiddenService.ENABLED)
|
||||
);
|
||||
|
||||
port = savedValues.getInt(HSContentProvider.HiddenService.PORT);
|
||||
fields.put(HSContentProvider.HiddenService.PORT, port);
|
||||
|
||||
service = mResolver.query(
|
||||
HSContentProvider.CONTENT_URI,
|
||||
HSContentProvider.PROJECTION,
|
||||
HSContentProvider.HiddenService.PORT + "=" + port,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
if (service == null || service.getCount() == 0) {
|
||||
mResolver.insert(HSContentProvider.CONTENT_URI, fields);
|
||||
} else {
|
||||
mResolver.update(
|
||||
HSContentProvider.CONTENT_URI,
|
||||
fields,
|
||||
HSContentProvider.HiddenService.PORT + "=" + port,
|
||||
null
|
||||
);
|
||||
|
||||
service.close();
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
Toast.makeText(mContext, R.string.backup_restored, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
public void restoreKeyBackup(int hsPort, Uri hsKeyPath) {
|
||||
File mHSBasePath = new File(
|
||||
mContext.getFilesDir().getAbsolutePath(),
|
||||
TorServiceConstants.HIDDEN_SERVICES_DIR
|
||||
);
|
||||
|
||||
File serviceDir = new File(mHSBasePath, "hs" + hsPort);
|
||||
|
||||
if (!serviceDir.isDirectory())
|
||||
serviceDir.mkdirs();
|
||||
|
||||
try {
|
||||
ParcelFileDescriptor mInputPFD = mContext.getContentResolver().openFileDescriptor(hsKeyPath, "r");
|
||||
InputStream fileStream = new FileInputStream(mInputPFD.getFileDescriptor());
|
||||
OutputStream file = new FileOutputStream(serviceDir.getAbsolutePath() + "/private_key");
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int length;
|
||||
while ((length = fileStream.read(buffer)) > 0) {
|
||||
file.write(buffer, 0, length);
|
||||
}
|
||||
file.close();
|
||||
|
||||
} catch (IOException | NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void restoreCookieBackup(File p) {
|
||||
File config = new File(p.getAbsolutePath());
|
||||
FileInputStream stream;
|
||||
String jString = null;
|
||||
|
||||
try {
|
||||
stream = new FileInputStream(config);
|
||||
FileChannel fc = stream.getChannel();
|
||||
MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
|
||||
jString = Charset.defaultCharset().decode(bb).toString();
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (jString == null)
|
||||
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
||||
|
||||
try {
|
||||
JSONObject savedValues = new JSONObject(jString);
|
||||
ContentValues fields = new ContentValues();
|
||||
|
||||
fields.put(
|
||||
CookieContentProvider.ClientCookie.DOMAIN,
|
||||
savedValues.getString(CookieContentProvider.ClientCookie.DOMAIN)
|
||||
);
|
||||
|
||||
fields.put(
|
||||
CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE,
|
||||
savedValues.getString(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE)
|
||||
);
|
||||
|
||||
fields.put(
|
||||
CookieContentProvider.ClientCookie.ENABLED,
|
||||
savedValues.getInt(CookieContentProvider.ClientCookie.ENABLED)
|
||||
);
|
||||
|
||||
mResolver.insert(CookieContentProvider.CONTENT_URI, fields);
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
Toast.makeText(mContext, R.string.backup_restored, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
public String createCookieBackup(String domain, String cookie, Integer enabled) {
|
||||
File storage_path = ExternalStorage.getOrCreateBackupDir();
|
||||
String backupFile = storage_path.getAbsolutePath() + '/' + domain.replace(".onion", ".json");
|
||||
|
||||
JSONObject backup = new JSONObject();
|
||||
try {
|
||||
backup.put(CookieContentProvider.ClientCookie.DOMAIN, domain);
|
||||
backup.put(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE, cookie);
|
||||
backup.put(CookieContentProvider.ClientCookie.ENABLED, enabled);
|
||||
FileWriter file = new FileWriter(backupFile);
|
||||
file.write(backup.toString());
|
||||
file.close();
|
||||
} catch (JSONException | IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
return backupFile;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package org.torproject.android.ui.hiddenservices.backup;
|
||||
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class ZipIt {
|
||||
private static final int BUFFER = 2048;
|
||||
|
||||
private String[] _files;
|
||||
private String _zipFile;
|
||||
|
||||
public ZipIt(@Nullable String[] files, @NonNull String zipFile) {
|
||||
_files = files;
|
||||
_zipFile = zipFile;
|
||||
}
|
||||
|
||||
public boolean zip() {
|
||||
try {
|
||||
BufferedInputStream origin;
|
||||
FileOutputStream dest = new FileOutputStream(_zipFile);
|
||||
|
||||
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
|
||||
|
||||
byte data[] = new byte[BUFFER];
|
||||
|
||||
for (String _file : _files) {
|
||||
FileInputStream fi = new FileInputStream(_file);
|
||||
origin = new BufferedInputStream(fi, BUFFER);
|
||||
ZipEntry entry = new ZipEntry(_file.substring(_file.lastIndexOf("/") + 1));
|
||||
out.putNextEntry(entry);
|
||||
int count;
|
||||
while ((count = origin.read(data, 0, BUFFER)) != -1) {
|
||||
out.write(data, 0, count);
|
||||
}
|
||||
origin.close();
|
||||
}
|
||||
|
||||
out.close();
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean unzip(String output_path) {
|
||||
InputStream is;
|
||||
ZipInputStream zis;
|
||||
|
||||
try {
|
||||
String filename;
|
||||
is = new FileInputStream(_zipFile);
|
||||
zis = new ZipInputStream(new BufferedInputStream(is));
|
||||
ZipEntry ze;
|
||||
byte[] buffer = new byte[1024];
|
||||
int count;
|
||||
|
||||
while ((ze = zis.getNextEntry()) != null) {
|
||||
// zapis do souboru
|
||||
filename = ze.getName();
|
||||
|
||||
// Need to create directories if not exists, or
|
||||
// it will generate an Exception...
|
||||
if (ze.isDirectory()) {
|
||||
File fmd = new File(output_path + "/" + filename);
|
||||
fmd.mkdirs();
|
||||
continue;
|
||||
}
|
||||
|
||||
FileOutputStream fout = new FileOutputStream(output_path + "/" + filename);
|
||||
|
||||
// cteni zipu a zapis
|
||||
while ((count = zis.read(buffer)) != -1) {
|
||||
fout.write(buffer, 0, count);
|
||||
}
|
||||
|
||||
fout.close();
|
||||
zis.closeEntry();
|
||||
}
|
||||
|
||||
zis.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package org.torproject.android.ui.hiddenservices.database;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
public class HSDatabase extends SQLiteOpenHelper {
|
||||
|
||||
public static final String HS_DATA_TABLE_NAME = "hs_data";
|
||||
public static final String HS_CLIENT_COOKIE_TABLE_NAME = "hs_client_cookie";
|
||||
private static final int DATABASE_VERSION = 2;
|
||||
private static final String DATABASE_NAME = "hidden_services";
|
||||
private static final String HS_DATA_TABLE_CREATE =
|
||||
"CREATE TABLE " + HS_DATA_TABLE_NAME + " (" +
|
||||
"_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
||||
"name TEXT, " +
|
||||
"domain TEXT, " +
|
||||
"onion_port INTEGER, " +
|
||||
"auth_cookie INTEGER DEFAULT 0, " +
|
||||
"auth_cookie_value TEXT, " +
|
||||
"created_by_user INTEGER DEFAULT 0, " +
|
||||
"enabled INTEGER DEFAULT 1, " +
|
||||
"port INTEGER);";
|
||||
|
||||
private static final String HS_CLIENT_COOKIE_TABLE_CREATE =
|
||||
"CREATE TABLE " + HS_CLIENT_COOKIE_TABLE_NAME + " (" +
|
||||
"_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
||||
"domain TEXT, " +
|
||||
"auth_cookie_value TEXT, " +
|
||||
"enabled INTEGER DEFAULT 1);";
|
||||
|
||||
public HSDatabase(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(HS_DATA_TABLE_CREATE);
|
||||
db.execSQL(HS_CLIENT_COOKIE_TABLE_CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.torproject.android.R;
|
||||
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
||||
|
||||
public class AddCookieDialog extends DialogFragment {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_add_client_cookie_dialog, null);
|
||||
|
||||
final AlertDialog addCookieDialog = new AlertDialog.Builder(getActivity())
|
||||
.setView(dialog_view)
|
||||
.setTitle(R.string.client_cookies)
|
||||
.create();
|
||||
|
||||
Button save = (Button) dialog_view.findViewById(R.id.cookie_dialog_save);
|
||||
save.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
String onion = ((EditText) dialog_view.findViewById(R.id.cookie_onion)).getText().toString();
|
||||
String cookie = ((EditText) dialog_view.findViewById(R.id.cookie_value)).getText().toString();
|
||||
|
||||
if (checkInput(onion, cookie)) {
|
||||
saveData(onion, cookie);
|
||||
Toast.makeText(
|
||||
v.getContext(), R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG
|
||||
).show();
|
||||
addCookieDialog.dismiss();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Button cancel = (Button) dialog_view.findViewById(R.id.cookie_dialog_cancel);
|
||||
cancel.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
addCookieDialog.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
return addCookieDialog;
|
||||
}
|
||||
|
||||
private boolean checkInput(String onion, String cookie) {
|
||||
|
||||
boolean is_set = ((onion != null && onion.length() > 0) && (cookie != null && cookie.length() > 0));
|
||||
if (!is_set) {
|
||||
Toast.makeText(getContext(), R.string.fields_can_t_be_empty, Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!onion.matches("([a-z0-9]{16})\\.onion")) {
|
||||
Toast.makeText(getContext(), R.string.invalid_onion_address, Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void saveData(String domain, String cookie) {
|
||||
|
||||
ContentValues fields = new ContentValues();
|
||||
fields.put(CookieContentProvider.ClientCookie.DOMAIN, domain);
|
||||
fields.put(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE, cookie);
|
||||
|
||||
ContentResolver cr = getContext().getContentResolver();
|
||||
|
||||
cr.insert(CookieContentProvider.CONTENT_URI, fields);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.torproject.android.R;
|
||||
import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
|
||||
import org.torproject.android.ui.hiddenservices.permissions.PermissionManager;
|
||||
|
||||
public class CookieActionsDialog extends DialogFragment {
|
||||
public static final int WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTION_DIALOG = 4;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final Bundle arguments = getArguments();
|
||||
|
||||
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_cookie_actions, null);
|
||||
final AlertDialog actionDialog = new AlertDialog.Builder(getActivity())
|
||||
.setView(dialog_view)
|
||||
.setTitle(R.string.client_cookies)
|
||||
.create();
|
||||
|
||||
Button backup = (Button) dialog_view.findViewById(R.id.btn_cookie_backup);
|
||||
backup.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
Context mContext = v.getContext();
|
||||
|
||||
if (PermissionManager.isLollipopOrHigher()
|
||||
&& !PermissionManager.hasExternalWritePermission(mContext)) {
|
||||
|
||||
PermissionManager.requestExternalWritePermissions(
|
||||
getActivity(), WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTION_DIALOG);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
BackupUtils backup_utils = new BackupUtils(mContext);
|
||||
String backupPath = backup_utils.createCookieBackup(
|
||||
arguments.getString("domain"),
|
||||
arguments.getString("auth_cookie_value"),
|
||||
arguments.getInt("enabled")
|
||||
);
|
||||
|
||||
if (backupPath == null || backupPath.length() < 1) {
|
||||
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
||||
actionDialog.dismiss();
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.makeText(mContext, R.string.backup_saved_at_external_storage, Toast.LENGTH_LONG).show();
|
||||
|
||||
Uri selectedUri = Uri.parse(backupPath.substring(0, backupPath.lastIndexOf("/")));
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(selectedUri, "resource/folder");
|
||||
|
||||
if (intent.resolveActivityInfo(mContext.getPackageManager(), 0) != null) {
|
||||
startActivity(intent);
|
||||
} else {
|
||||
Toast.makeText(mContext, R.string.filemanager_not_available, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
actionDialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
Button delete = (Button) dialog_view.findViewById(R.id.btn_cookie_delete);
|
||||
delete.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
CookieDeleteDialog dialog = new CookieDeleteDialog();
|
||||
dialog.setArguments(arguments);
|
||||
dialog.show(getFragmentManager(), "CookieDeleteDialog");
|
||||
actionDialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
Button cancel = (Button) dialog_view.findViewById(R.id.btn_cookie_cancel);
|
||||
cancel.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
actionDialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
return actionDialog;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
import org.torproject.android.R;
|
||||
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
||||
|
||||
public class CookieDeleteDialog extends DialogFragment {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final Bundle arguments = getArguments();
|
||||
final Context context = getContext();
|
||||
|
||||
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
switch (which) {
|
||||
case DialogInterface.BUTTON_POSITIVE:
|
||||
// Delete from db
|
||||
context.getContentResolver().delete(
|
||||
CookieContentProvider.CONTENT_URI,
|
||||
CookieContentProvider.ClientCookie._ID + "=" + arguments.getInt("_id"),
|
||||
null
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case DialogInterface.BUTTON_NEGATIVE:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return new AlertDialog.Builder(context)
|
||||
.setMessage(R.string.confirm_cookie_deletion)
|
||||
.setPositiveButton(R.string.btn_okay, dialogClickListener)
|
||||
.setNegativeButton(R.string.btn_cancel, dialogClickListener)
|
||||
.create();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.torproject.android.R;
|
||||
import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
|
||||
import org.torproject.android.ui.hiddenservices.permissions.PermissionManager;
|
||||
|
||||
public class HSActionsDialog extends DialogFragment {
|
||||
public static final int WRITE_EXTERNAL_STORAGE_FROM_ACTION_DIALOG = 2;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final Bundle arguments = getArguments();
|
||||
|
||||
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_actions, null);
|
||||
final AlertDialog actionDialog = new AlertDialog.Builder(getActivity())
|
||||
.setView(dialog_view)
|
||||
.setTitle(R.string.hidden_services)
|
||||
.create();
|
||||
|
||||
Button backup = (Button) dialog_view.findViewById(R.id.btn_hs_backup);
|
||||
backup.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
Context mContext = v.getContext();
|
||||
|
||||
if (PermissionManager.isLollipopOrHigher()
|
||||
&& !PermissionManager.hasExternalWritePermission(mContext)) {
|
||||
|
||||
PermissionManager.requestExternalWritePermissions(
|
||||
getActivity(), WRITE_EXTERNAL_STORAGE_FROM_ACTION_DIALOG);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
BackupUtils hsutils = new BackupUtils(mContext);
|
||||
String backupPath = hsutils.createZipBackup(Integer.parseInt(arguments.getString("port")));
|
||||
|
||||
if (backupPath == null || backupPath.length() < 1) {
|
||||
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
||||
actionDialog.dismiss();
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.makeText(mContext, R.string.backup_saved_at_external_storage, Toast.LENGTH_LONG).show();
|
||||
|
||||
Uri selectedUri = Uri.parse(backupPath.substring(0, backupPath.lastIndexOf("/")));
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(selectedUri, "resource/folder");
|
||||
|
||||
if (intent.resolveActivityInfo(mContext.getPackageManager(), 0) != null) {
|
||||
startActivity(intent);
|
||||
} else {
|
||||
Toast.makeText(mContext, R.string.filemanager_not_available, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
actionDialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
Button copy = (Button) dialog_view.findViewById(R.id.btn_hs_clipboard);
|
||||
copy.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
Context mContext = v.getContext();
|
||||
ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText("onion", arguments.getString("onion"));
|
||||
clipboard.setPrimaryClip(clip);
|
||||
Toast.makeText(mContext, R.string.done, Toast.LENGTH_LONG).show();
|
||||
actionDialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
Button showAuth = (Button) dialog_view.findViewById(R.id.bt_hs_show_auth);
|
||||
showAuth.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
String auth_cookie_value = arguments.getString("auth_cookie_value");
|
||||
|
||||
if (arguments.getInt("auth_cookie") == 1) {
|
||||
if (auth_cookie_value == null || auth_cookie_value.length() < 1) {
|
||||
Toast.makeText(
|
||||
v.getContext(), R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG
|
||||
).show();
|
||||
} else {
|
||||
HSCookieDialog dialog = new HSCookieDialog();
|
||||
dialog.setArguments(arguments);
|
||||
dialog.show(getFragmentManager(), "HSCookieDialog");
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(
|
||||
v.getContext(), R.string.auth_cookie_was_not_configured, Toast.LENGTH_LONG
|
||||
).show();
|
||||
}
|
||||
|
||||
actionDialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
Button delete = (Button) dialog_view.findViewById(R.id.btn_hs_delete);
|
||||
delete.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
HSDeleteDialog dialog = new HSDeleteDialog();
|
||||
dialog.setArguments(arguments);
|
||||
dialog.show(getFragmentManager(), "HSDeleteDialog");
|
||||
actionDialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
Button cancel = (Button) dialog_view.findViewById(R.id.btn_hs_cancel);
|
||||
cancel.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
actionDialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
return actionDialog;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.zxing.integration.android.IntentIntegrator;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.torproject.android.R;
|
||||
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
||||
|
||||
public class HSCookieDialog extends DialogFragment {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_cookie, null);
|
||||
|
||||
final Bundle arguments = getArguments();
|
||||
final String auth_cookie_value = arguments.getString("auth_cookie_value");
|
||||
|
||||
final AlertDialog cookieDialog = new AlertDialog.Builder(getActivity())
|
||||
.setView(dialog_view)
|
||||
.create();
|
||||
|
||||
TextView cookie = (TextView) dialog_view.findViewById(R.id.hs_cookie);
|
||||
cookie.setText(auth_cookie_value);
|
||||
|
||||
Button clipboard = (Button) dialog_view.findViewById(R.id.hs_cookie_to_clipboard);
|
||||
clipboard.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
Context mContext = v.getContext();
|
||||
ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText("cookie", auth_cookie_value);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
Toast.makeText(mContext, R.string.done, Toast.LENGTH_LONG).show();
|
||||
cookieDialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
Button shareQR = (Button) dialog_view.findViewById(R.id.hs_cookie_to_qr);
|
||||
shareQR.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
try {
|
||||
JSONObject backup = new JSONObject();
|
||||
backup.put(CookieContentProvider.ClientCookie.DOMAIN, arguments.getString("onion"));
|
||||
backup.put(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE, arguments.getString("auth_cookie_value"));
|
||||
|
||||
IntentIntegrator integrator = new IntentIntegrator(getActivity());
|
||||
integrator.shareText(backup.toString());
|
||||
|
||||
} catch (JSONException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
cookieDialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
Button cancel = (Button) dialog_view.findViewById(R.id.hs_cookie_cancel);
|
||||
cancel.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
cookieDialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
return cookieDialog;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.torproject.android.R;
|
||||
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
||||
|
||||
public class HSDataDialog extends DialogFragment {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
// Get the layout
|
||||
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_data_dialog, null);
|
||||
|
||||
// Use the Builder class for convenient dialog construction
|
||||
final AlertDialog serviceDataDialog = new AlertDialog.Builder(getActivity())
|
||||
.setView(dialog_view)
|
||||
.setTitle(R.string.hidden_services)
|
||||
.create();
|
||||
|
||||
// Buttons action
|
||||
Button save = (Button) dialog_view.findViewById(R.id.HSDialogSave);
|
||||
save.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
String serverName = ((EditText) dialog_view.findViewById(R.id.hsName)).getText().toString();
|
||||
Integer localPort = Integer.parseInt(
|
||||
((EditText) dialog_view.findViewById(R.id.hsLocalPort)).getText().toString()
|
||||
);
|
||||
Integer onionPort = Integer.parseInt(
|
||||
((EditText) dialog_view.findViewById(R.id.hsOnionPort)).getText().toString()
|
||||
);
|
||||
|
||||
Boolean authCookie = ((CheckBox) dialog_view.findViewById(R.id.hsAuth)).isChecked();
|
||||
|
||||
if (checkInput(serverName, localPort, onionPort)) {
|
||||
saveData(serverName, localPort, onionPort, authCookie);
|
||||
Toast.makeText(
|
||||
v.getContext(), R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG
|
||||
).show();
|
||||
serviceDataDialog.dismiss();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Button cancel = (Button) dialog_view.findViewById(R.id.HSDialogCancel);
|
||||
cancel.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
serviceDataDialog.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
return serviceDataDialog;
|
||||
}
|
||||
|
||||
private boolean checkInput(String serverName, Integer local, Integer remote) {
|
||||
boolean is_ok = true;
|
||||
Integer error_msg = 0;
|
||||
|
||||
if ((local < 1 || local > 65535) || (remote < 1 || remote > 65535)) {
|
||||
error_msg = R.string.invalid_port;
|
||||
is_ok = false;
|
||||
}
|
||||
|
||||
if (serverName == null || serverName.length() < 1) {
|
||||
error_msg = R.string.name_can_t_be_empty;
|
||||
is_ok = false;
|
||||
}
|
||||
|
||||
if (!is_ok) {
|
||||
Toast.makeText(getContext(), error_msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
return is_ok;
|
||||
}
|
||||
|
||||
private void saveData(String name, Integer local, Integer remote, Boolean authCookie) {
|
||||
|
||||
ContentValues fields = new ContentValues();
|
||||
fields.put(HSContentProvider.HiddenService.NAME, name);
|
||||
fields.put(HSContentProvider.HiddenService.PORT, local);
|
||||
fields.put(HSContentProvider.HiddenService.ONION_PORT, remote);
|
||||
fields.put(HSContentProvider.HiddenService.AUTH_COOKIE, authCookie);
|
||||
fields.put(HSContentProvider.HiddenService.CREATED_BY_USER, 1);
|
||||
|
||||
ContentResolver cr = getContext().getContentResolver();
|
||||
|
||||
cr.insert(HSContentProvider.CONTENT_URI, fields);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
import org.torproject.android.R;
|
||||
import org.torproject.android.service.TorServiceConstants;
|
||||
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class HSDeleteDialog extends DialogFragment {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final Bundle arguments = getArguments();
|
||||
final Context context = getContext();
|
||||
|
||||
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
switch (which) {
|
||||
case DialogInterface.BUTTON_POSITIVE:
|
||||
// Delete from db
|
||||
context.getContentResolver().delete(
|
||||
HSContentProvider.CONTENT_URI,
|
||||
HSContentProvider.HiddenService._ID + "=" + arguments.getInt("_id"),
|
||||
null
|
||||
);
|
||||
|
||||
// Delete from interal storage
|
||||
String base = context.getFilesDir().getAbsolutePath() + "/" + TorServiceConstants.HIDDEN_SERVICES_DIR;
|
||||
File dir = new File(base, "hs" + arguments.getString("port"));
|
||||
|
||||
if (dir.isDirectory()) {
|
||||
String[] children = dir.list();
|
||||
for (String aChildren : children) {
|
||||
new File(dir, aChildren).delete();
|
||||
}
|
||||
dir.delete();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DialogInterface.BUTTON_NEGATIVE:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return new AlertDialog.Builder(context)
|
||||
.setMessage(R.string.confirm_service_deletion)
|
||||
.setPositiveButton(R.string.btn_okay, dialogClickListener)
|
||||
.setNegativeButton(R.string.btn_cancel, dialogClickListener)
|
||||
.create();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import org.torproject.android.R;
|
||||
import org.torproject.android.ui.hiddenservices.adapters.BackupAdapter;
|
||||
import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
|
||||
import org.torproject.android.ui.hiddenservices.storage.ExternalStorage;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class SelectCookieBackupDialog extends DialogFragment {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
AlertDialog.Builder cookieBackupDialog = new AlertDialog.Builder(getActivity());
|
||||
|
||||
cookieBackupDialog.setTitle(R.string.restore_backup);
|
||||
|
||||
File backupDir = ExternalStorage.getOrCreateBackupDir();
|
||||
File[] files = null;
|
||||
|
||||
try {
|
||||
files = backupDir.listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.toLowerCase().endsWith(".json");
|
||||
}
|
||||
});
|
||||
} catch (NullPointerException e) {
|
||||
// Silent block
|
||||
}
|
||||
|
||||
if (files == null || files.length < 1) {
|
||||
cookieBackupDialog.setMessage(R.string.create_a_backup_first);
|
||||
cookieBackupDialog.setNegativeButton(R.string.btn_cancel, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
return cookieBackupDialog.create();
|
||||
}
|
||||
|
||||
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_backups_list, null);
|
||||
|
||||
cookieBackupDialog.setView(dialog_view);
|
||||
cookieBackupDialog.setPositiveButton(R.string.btn_okay, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
ListView backups = (ListView) dialog_view.findViewById(R.id.listview_hs_backups);
|
||||
|
||||
List<File> json_backups = new ArrayList<>();
|
||||
Collections.addAll(json_backups, files);
|
||||
|
||||
backups.setAdapter(new BackupAdapter(getContext(), R.layout.layout_hs_backups_list_item, json_backups));
|
||||
backups.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
BackupUtils backupUtils = new BackupUtils(view.getContext().getApplicationContext());
|
||||
File p = (File) parent.getItemAtPosition(position);
|
||||
backupUtils.restoreCookieBackup(p);
|
||||
}
|
||||
});
|
||||
|
||||
return cookieBackupDialog.create();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import org.torproject.android.R;
|
||||
import org.torproject.android.ui.hiddenservices.adapters.BackupAdapter;
|
||||
import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
|
||||
import org.torproject.android.ui.hiddenservices.storage.ExternalStorage;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class SelectHSBackupDialog extends DialogFragment {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
AlertDialog.Builder backupsDialog = new AlertDialog.Builder(getActivity());
|
||||
|
||||
backupsDialog.setTitle(R.string.restore_backup);
|
||||
|
||||
File backupDir = ExternalStorage.getOrCreateBackupDir();
|
||||
File[] files = null;
|
||||
|
||||
try {
|
||||
files = backupDir.listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.toLowerCase().endsWith(".zip");
|
||||
}
|
||||
});
|
||||
} catch (NullPointerException e) {
|
||||
// Silent block
|
||||
}
|
||||
|
||||
if (files == null || files.length < 1) {
|
||||
backupsDialog.setMessage(R.string.create_a_backup_first);
|
||||
backupsDialog.setNegativeButton(R.string.btn_cancel, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
return backupsDialog.create();
|
||||
}
|
||||
|
||||
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_backups_list, null);
|
||||
|
||||
backupsDialog.setView(dialog_view);
|
||||
backupsDialog.setPositiveButton(R.string.btn_okay, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
ListView backups = (ListView) dialog_view.findViewById(R.id.listview_hs_backups);
|
||||
|
||||
List<File> zips = new ArrayList<>();
|
||||
Collections.addAll(zips, files);
|
||||
|
||||
backups.setAdapter(new BackupAdapter(getContext(), R.layout.layout_hs_backups_list_item, zips));
|
||||
backups.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
BackupUtils backupUtils = new BackupUtils(view.getContext().getApplicationContext());
|
||||
File p = (File) parent.getItemAtPosition(position);
|
||||
backupUtils.restoreZipBackup(p);
|
||||
}
|
||||
});
|
||||
|
||||
return backupsDialog.create();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package org.torproject.android.ui.hiddenservices.permissions;
|
||||
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.PowerManager;
|
||||
import android.provider.Settings;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.view.View;
|
||||
|
||||
import org.torproject.android.R;
|
||||
|
||||
public class PermissionManager {
|
||||
public static int VERY_LONG_LENGTH = 6000;
|
||||
|
||||
public static boolean isLollipopOrHigher() {
|
||||
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP);
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public static boolean hasExternalWritePermission(Context context) {
|
||||
return (context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
|
||||
}
|
||||
|
||||
public static void requestExternalWritePermissions(FragmentActivity activity, int action) {
|
||||
final int mAction = action;
|
||||
final FragmentActivity mActivity = activity;
|
||||
|
||||
if (ActivityCompat.shouldShowRequestPermissionRationale
|
||||
(mActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
Snackbar.make(mActivity.findViewById(android.R.id.content),
|
||||
R.string.please_grant_permissions_for_external_storage,
|
||||
Snackbar.LENGTH_INDEFINITE).setAction(R.string.activate,
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ActivityCompat.requestPermissions(mActivity,
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
mAction);
|
||||
}
|
||||
}).show();
|
||||
} else {
|
||||
ActivityCompat.requestPermissions(mActivity,
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
mAction);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
public static void requestBatteryPermmssions(FragmentActivity activity, Context context) {
|
||||
final Context mContext = context;
|
||||
final String packageName = mContext.getPackageName();
|
||||
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
|
||||
|
||||
if (pm.isIgnoringBatteryOptimizations(packageName))
|
||||
return;
|
||||
|
||||
Snackbar.make(activity.findViewById(android.R.id.content),
|
||||
R.string.consider_disable_battery_optimizations,
|
||||
VERY_LONG_LENGTH).setAction(R.string.disable,
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
||||
intent.setData(Uri.parse("package:" + packageName));
|
||||
mContext.startActivity(intent);
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
public static void requestDropBatteryPermmssions(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))
|
||||
return;
|
||||
|
||||
Snackbar.make(activity.findViewById(android.R.id.content),
|
||||
R.string.consider_enable_battery_optimizations,
|
||||
VERY_LONG_LENGTH).setAction(R.string.enable,
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
|
||||
mContext.startActivity(intent);
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
package org.torproject.android.ui.hiddenservices.providers;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.torproject.android.ui.hiddenservices.database.HSDatabase;
|
||||
|
||||
|
||||
public class CookieContentProvider extends ContentProvider {
|
||||
public static final String[] PROJECTION = new String[]{
|
||||
ClientCookie._ID,
|
||||
ClientCookie.DOMAIN,
|
||||
ClientCookie.AUTH_COOKIE_VALUE,
|
||||
ClientCookie.ENABLED
|
||||
};
|
||||
private static final String AUTH = "org.torproject.android.ui.hiddenservices.providers.cookie";
|
||||
public static final Uri CONTENT_URI =
|
||||
Uri.parse("content://" + AUTH + "/cookie");
|
||||
//UriMatcher
|
||||
private static final int COOKIES = 1;
|
||||
private static final int COOKIE_ID = 2;
|
||||
|
||||
private static final UriMatcher uriMatcher;
|
||||
|
||||
static {
|
||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
uriMatcher.addURI(AUTH, "hs", COOKIES);
|
||||
uriMatcher.addURI(AUTH, "hs/#", COOKIE_ID);
|
||||
}
|
||||
|
||||
private HSDatabase mServervices;
|
||||
private Context mContext;
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
mContext = getContext();
|
||||
mServervices = new HSDatabase(mContext);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||
String where = selection;
|
||||
if (uriMatcher.match(uri) == COOKIE_ID) {
|
||||
where = "_id=" + uri.getLastPathSegment();
|
||||
}
|
||||
|
||||
SQLiteDatabase db = mServervices.getReadableDatabase();
|
||||
|
||||
return db.query(HSDatabase.HS_CLIENT_COOKIE_TABLE_NAME, projection, where,
|
||||
selectionArgs, null, null, sortOrder);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getType(@NonNull Uri uri) {
|
||||
int match = uriMatcher.match(uri);
|
||||
|
||||
switch (match) {
|
||||
case COOKIES:
|
||||
return "vnd.android.cursor.dir/vnd.torproject.cookies";
|
||||
case COOKIE_ID:
|
||||
return "vnd.android.cursor.item/vnd.torproject.cookie";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Uri insert(@NonNull Uri uri, ContentValues values) {
|
||||
long regId;
|
||||
|
||||
SQLiteDatabase db = mServervices.getWritableDatabase();
|
||||
|
||||
regId = db.insert(HSDatabase.HS_CLIENT_COOKIE_TABLE_NAME, null, values);
|
||||
|
||||
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
||||
|
||||
return ContentUris.withAppendedId(CONTENT_URI, regId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
|
||||
|
||||
String where = selection;
|
||||
if (uriMatcher.match(uri) == COOKIE_ID) {
|
||||
where = "_id=" + uri.getLastPathSegment();
|
||||
}
|
||||
|
||||
SQLiteDatabase db = mServervices.getWritableDatabase();
|
||||
|
||||
Integer rows = db.delete(HSDatabase.HS_CLIENT_COOKIE_TABLE_NAME, where, selectionArgs);
|
||||
|
||||
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
||||
|
||||
return rows;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
SQLiteDatabase db = mServervices.getWritableDatabase();
|
||||
|
||||
String where = selection;
|
||||
if (uriMatcher.match(uri) == COOKIE_ID) {
|
||||
where = "_id=" + uri.getLastPathSegment();
|
||||
}
|
||||
|
||||
Integer rows = db.update(HSDatabase.HS_CLIENT_COOKIE_TABLE_NAME, values, where, null);
|
||||
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
public static final class ClientCookie implements BaseColumns {
|
||||
public static final String DOMAIN = "domain";
|
||||
public static final String AUTH_COOKIE_VALUE = "auth_cookie_value";
|
||||
public static final String ENABLED = "enabled";
|
||||
|
||||
private ClientCookie() {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
package org.torproject.android.ui.hiddenservices.providers;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.torproject.android.ui.hiddenservices.database.HSDatabase;
|
||||
|
||||
|
||||
public class HSContentProvider extends ContentProvider {
|
||||
public static final String[] PROJECTION = new String[]{
|
||||
HiddenService._ID,
|
||||
HiddenService.NAME,
|
||||
HiddenService.PORT,
|
||||
HiddenService.DOMAIN,
|
||||
HiddenService.ONION_PORT,
|
||||
HiddenService.AUTH_COOKIE,
|
||||
HiddenService.AUTH_COOKIE_VALUE,
|
||||
HiddenService.CREATED_BY_USER,
|
||||
HiddenService.ENABLED
|
||||
};
|
||||
private static final String AUTH = "org.torproject.android.ui.hiddenservices.providers";
|
||||
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;
|
||||
|
||||
private static final UriMatcher uriMatcher;
|
||||
|
||||
static {
|
||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
uriMatcher.addURI(AUTH, "hs", ONIONS);
|
||||
uriMatcher.addURI(AUTH, "hs/#", ONION_ID);
|
||||
}
|
||||
|
||||
private HSDatabase mServervices;
|
||||
private Context mContext;
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
mContext = getContext();
|
||||
mServervices = new HSDatabase(mContext);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||
String where = selection;
|
||||
if (uriMatcher.match(uri) == ONION_ID) {
|
||||
where = "_id=" + uri.getLastPathSegment();
|
||||
}
|
||||
|
||||
SQLiteDatabase db = mServervices.getReadableDatabase();
|
||||
|
||||
return db.query(HSDatabase.HS_DATA_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, ContentValues values) {
|
||||
long regId;
|
||||
|
||||
SQLiteDatabase db = mServervices.getWritableDatabase();
|
||||
|
||||
regId = db.insert(HSDatabase.HS_DATA_TABLE_NAME, null, values);
|
||||
|
||||
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
||||
|
||||
return ContentUris.withAppendedId(CONTENT_URI, regId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
|
||||
|
||||
String where = selection;
|
||||
if (uriMatcher.match(uri) == ONION_ID) {
|
||||
where = "_id=" + uri.getLastPathSegment();
|
||||
}
|
||||
|
||||
SQLiteDatabase db = mServervices.getWritableDatabase();
|
||||
|
||||
Integer rows = db.delete(HSDatabase.HS_DATA_TABLE_NAME, where, selectionArgs);
|
||||
|
||||
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
||||
|
||||
return rows;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
SQLiteDatabase db = mServervices.getWritableDatabase();
|
||||
|
||||
String where = selection;
|
||||
if (uriMatcher.match(uri) == ONION_ID) {
|
||||
where = "_id=" + uri.getLastPathSegment();
|
||||
}
|
||||
|
||||
Integer rows = db.update(HSDatabase.HS_DATA_TABLE_NAME, values, where, null);
|
||||
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
public static final class HiddenService 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() {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package org.torproject.android.ui.hiddenservices.storage;
|
||||
|
||||
import android.os.Environment;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class ExternalStorage {
|
||||
private static final String ORBOT_BACKUPS_DIR = "Orbot";
|
||||
|
||||
public static File getOrCreateBackupDir() {
|
||||
if (!isExternalStorageWritable())
|
||||
return null;
|
||||
|
||||
File dir = new File(Environment.getExternalStorageDirectory(), ORBOT_BACKUPS_DIR);
|
||||
|
||||
if (!dir.isDirectory() && !dir.mkdirs())
|
||||
return null;
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
/* Checks if external storage is available for read and write */
|
||||
public static boolean isExternalStorageWritable() {
|
||||
String state = Environment.getExternalStorageState();
|
||||
return Environment.MEDIA_MOUNTED.equals(state);
|
||||
}
|
||||
|
||||
/* Checks if external storage is available to at least read */
|
||||
public static boolean isExternalStorageReadable() {
|
||||
String state = Environment.getExternalStorageState();
|
||||
return Environment.MEDIA_MOUNTED.equals(state) ||
|
||||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context="org.torproject.android.ui.hiddenservices.ClientCookiesActivity">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/DefaultTheme.AppBarOverlay">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/DefaultTheme.PopupOverlay" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<include layout="@layout/layout_content_client_cookies" />
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="@dimen/fab_margin"
|
||||
app:srcCompat="@android:drawable/stat_notify_more"
|
||||
app:backgroundTint="@android:color/darker_gray" />
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="5dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/label_onion_name"
|
||||
android:text="@string/onion"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Widget.PopupMenu.Small"
|
||||
android:paddingLeft="5dp" />
|
||||
|
||||
<EditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text"
|
||||
android:ems="10"
|
||||
android:id="@+id/cookie_onion" />
|
||||
|
||||
<TextView
|
||||
android:text="@string/auth_cookie"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/label_cookie_value"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Widget.PopupMenu.Small"
|
||||
android:paddingLeft="5dp" />
|
||||
|
||||
<EditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:id="@+id/cookie_value"
|
||||
android:inputType="text" />
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<Button
|
||||
android:text="@string/btn_cancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/cookie_dialog_cancel"
|
||||
android:layout_weight="1"
|
||||
style="@style/Widget.AppCompat.Button.Borderless.Colored" />
|
||||
|
||||
<Button
|
||||
android:text="@string/save"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/cookie_dialog_save"
|
||||
android:layout_weight="1"
|
||||
style="@style/Widget.AppCompat.Button.Borderless.Colored" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="15dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cookie_onion"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="18sp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<Switch
|
||||
android:id="@+id/cookie_switch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:switchPadding="30dp" />
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/content_client_cookies"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:context="org.torproject.android.ui.hiddenservices.ClientCookiesActivity"
|
||||
tools:showIn="@layout/layout_activity_client_cookies">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/clien_cookies_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<Button
|
||||
android:text="@string/backup_cookie"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/btn_cookie_backup" />
|
||||
|
||||
<Button
|
||||
android:text="@string/delete_cookie"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/btn_cookie_delete" />
|
||||
|
||||
<Button
|
||||
android:text="@string/btn_cancel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/btn_cookie_cancel" />
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<Button
|
||||
android:text="@string/copy_address_to_clipboard"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/btn_hs_clipboard" />
|
||||
|
||||
<Button
|
||||
android:text="@string/show_auth_cookie"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/bt_hs_show_auth" />
|
||||
|
||||
<Button
|
||||
android:text="@string/backup_service"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/btn_hs_backup" />
|
||||
|
||||
<Button
|
||||
android:text="@string/delete_service"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/btn_hs_delete" />
|
||||
|
||||
<Button
|
||||
android:text="@string/btn_cancel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/btn_hs_cancel" />
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ListView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/listview_hs_backups"
|
||||
android:layout_marginTop="15dp" />
|
||||
</LinearLayout>
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/backup_list_item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="15dp"
|
||||
tools:paddingLeft="15dp"
|
||||
android:paddingRight="15dp"
|
||||
tools:paddingRight="15dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/backup_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="30sp"
|
||||
android:layout_marginBottom="10dp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/hs_cookie"
|
||||
android:paddingBottom="5dp"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingTop="5dp" />
|
||||
|
||||
<Button
|
||||
android:text="@string/copy_cookie_to_clipboard"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/hs_cookie_to_clipboard" />
|
||||
|
||||
<Button
|
||||
android:text="@string/share_as_qr"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/hs_cookie_to_qr" />
|
||||
|
||||
<Button
|
||||
android:text="@string/btn_cancel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/hs_cookie_cancel" />
|
||||
</LinearLayout>
|
|
@ -0,0 +1,87 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="5dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/HSNameLabel"
|
||||
android:text="@string/name"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Widget.PopupMenu.Small"
|
||||
android:paddingLeft="5dp" />
|
||||
|
||||
<EditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text"
|
||||
android:ems="10"
|
||||
android:id="@+id/hsName" />
|
||||
|
||||
<TextView
|
||||
android:text="@string/local_port"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/HSLocalPortLabel"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Widget.PopupMenu.Small"
|
||||
android:paddingLeft="5dp" />
|
||||
|
||||
<EditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:id="@+id/hsLocalPort"
|
||||
android:inputType="number" />
|
||||
|
||||
<TextView
|
||||
android:text="@string/onion_port"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/HSOnionPortLabel"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Widget.PopupMenu.Small"
|
||||
android:paddingLeft="5dp" />
|
||||
|
||||
<EditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:id="@+id/hsOnionPort"
|
||||
android:inputType="number" />
|
||||
|
||||
<CheckBox
|
||||
android:text="@string/auth_cookie"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/hsAuth"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="10dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<Button
|
||||
android:text="@string/btn_cancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/HSDialogCancel"
|
||||
android:layout_weight="1"
|
||||
style="@style/Widget.AppCompat.Button.Borderless.Colored" />
|
||||
|
||||
<Button
|
||||
android:text="@string/save"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/HSDialogSave"
|
||||
android:layout_weight="1"
|
||||
style="@style/Widget.AppCompat.Button.Borderless.Colored" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp"
|
||||
tools:paddingLeft="15dp"
|
||||
tools:paddingRight="15dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/hs_port"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingTop="10dp"
|
||||
android:textSize="35sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/hs_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:textSize="24sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/hs_onion"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:textSize="18sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/hs_switch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:switchPadding="30dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context="org.torproject.android.ui.hiddenservices.HiddenServicesActivity">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<include layout="@layout/layout_hs_list_view_main" />
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="@dimen/fab_margin"
|
||||
app:srcCompat="@android:drawable/stat_notify_more"
|
||||
app:backgroundTint="@android:color/darker_gray" />
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/content_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:context="org.torproject.android.ui.hiddenservices.HiddenServicesActivity"
|
||||
tools:showIn="@layout/layout_hs_list_view">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/onion_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/cookie_restore_backup"
|
||||
android:title="@string/restore_backup" />
|
||||
<item
|
||||
android:id="@+id/cookie_from_qr"
|
||||
android:title="@string/cookie_from_QR" />
|
||||
</menu>
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/hs_type"
|
||||
android:background="#ff00"
|
||||
android:title="@string/service_type"
|
||||
app:actionViewClass="android.widget.Spinner"
|
||||
app:showAsAction="always" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_restore_backup"
|
||||
android:title="@string/restore_backup" />
|
||||
</menu>
|
|
@ -44,6 +44,22 @@
|
|||
</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>
|
||||
</item>
|
||||
|
||||
<!--
|
||||
<item android:id="@+id/menu_promo_apps"
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<resources>
|
||||
|
||||
<string-array name="bridge_options">
|
||||
<item>Obfs4 (Preferido)</item>
|
||||
<item>Túnel a través de Azure</item>
|
||||
<item>Túnel a través de Amazon</item>
|
||||
<item>Obtener nuevos puentes</item>
|
||||
<item></item>
|
||||
</string-array>
|
||||
<string-array name="array_hs_types">
|
||||
<item>Usuarios</item>
|
||||
<item>Aplicaciones</item>
|
||||
</string-array>
|
||||
</resources>
|
|
@ -291,4 +291,36 @@ direcciones (o rangos). No prevalecen sobre las configuraciones de exclusión de
|
|||
<string name="standard_browser">Navegador estándar</string>
|
||||
<string name="note_only_standard_tor_bridges_work_on_intel_x86_atom_devices">NOTA: Sólo los repetidores puente (bridges) estándar de Tor funcionan en dispositivos Intel X86/ATOM</string>
|
||||
<string name="vpn_default_world">Mundo</string>
|
||||
|
||||
<string name="hidden_services">Servicios Ocultos</string>
|
||||
<string name="title_activity_hidden_services">Servicios Ocultos</string>
|
||||
<string name="menu_hidden_services">Servicios Ocultos</string>
|
||||
<string name="save">Guardar</string>
|
||||
<string name="ports">Puertos</string>
|
||||
<string name="local_port">Puerto Local</string>
|
||||
<string name="onion_port">Puerto Onion</string>
|
||||
<string name="name">Nombre</string>
|
||||
<string name="done">Hecho!</string>
|
||||
<string name="invalid_port">Puerto no vàlido</string>
|
||||
<string name="copy_address_to_clipboard">Copiar dirección a portapapeles</string>
|
||||
<string name="show_auth_cookie">Mostra cookie de autenticación</string>
|
||||
<string name="backup_service">Crear backup</string>
|
||||
<string name="delete_service">Borrar servicio</string>
|
||||
<string name="backup_saved_at_external_storage">Backup guardado en almacenamiento externo</string>
|
||||
<string name="backup_restored">Backup restaurado</string>
|
||||
<string name="filemanager_not_available">Gestor de archivos no disponible</string>
|
||||
<string name="please_grant_permissions_for_external_storage">Permitir permisos para almacenamiento externo</string>
|
||||
<string name="permission_granted">Permisos concedidos</string>
|
||||
<string name="permission_denied">Permisos denegados</string>
|
||||
<string name="restore_backup">Restaurar Backup</string>
|
||||
<string name="create_a_backup_first">Crear primero una copia de seguridad</string>
|
||||
<string name="name_can_t_be_empty">Nombre no puede estar vacio</string>
|
||||
<string name="start_tor_again_for_finish_the_process">Inicie Tor de nuevo para finalizar el proceso</string>
|
||||
<string name="confirm_service_deletion">Confirmar eliminación de servicio</string>
|
||||
<string name="click_again_for_backup">Haga clic de nuevo para realizar copias de seguridad</string>
|
||||
<string name="service_type">Tipo de servicio</string>
|
||||
<string name="auth_cookie">Cookie de autenticación</string>
|
||||
<string name="copy_cookie_to_clipboard">Copiar cookie al portapapeles</string>
|
||||
<string name="auth_cookie_was_not_configured">La cookie de autenticación no estaba configurada</string>
|
||||
<string name="please_restart_Orbot_to_enable_the_changes">Reinicie Orbot para habilitar los cambios</string>
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<resources></resources>
|
|
@ -0,0 +1,6 @@
|
|||
<resources>
|
||||
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||
(such as screen margins) for screens with more than 820dp of available width. This
|
||||
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||
<dimen name="activity_horizontal_margin">64dp</dimen>
|
||||
</resources>
|
|
@ -9,5 +9,8 @@
|
|||
<item>Get New Bridges</item>
|
||||
<item></item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="array_hs_types">
|
||||
<item>User services</item>
|
||||
<item>App services</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
/* //device/apps/common/assets/res/any/dimens.xml
|
||||
**
|
||||
** Copyright 2006, The Android Open Source Project
|
||||
|
@ -118,26 +117,27 @@
|
|||
<!-- The platform's desired minimum size for a dialog's width when it
|
||||
is along the major axis (that is the screen is landscape). This may
|
||||
be either a fraction or a dimension. -->
|
||||
<item type="dimen" name="dialog_min_width_major">65%</item>
|
||||
<item name="dialog_min_width_major" type="dimen">65%</item>
|
||||
|
||||
<!-- The platform's desired fixed width for a dialog along the major axis
|
||||
(the screen is in landscape). This may be either a fraction or a dimension.-->
|
||||
<item type="dimen" name="dialog_fixed_width_major">320dp</item>
|
||||
<item name="dialog_fixed_width_major" type="dimen">320dp</item>
|
||||
<!-- The platform's desired fixed width for a dialog along the minor axis
|
||||
(the screen is in portrait). This may be either a fraction or a dimension.-->
|
||||
<item type="dimen" name="dialog_fixed_width_minor">320dp</item>
|
||||
<item name="dialog_fixed_width_minor" type="dimen">320dp</item>
|
||||
<!-- The platform's desired fixed height for a dialog along the major axis
|
||||
(the screen is in portrait). This may be either a fraction or a dimension.-->
|
||||
<item type="dimen" name="dialog_fixed_height_major">80%</item>
|
||||
<item name="dialog_fixed_height_major" type="dimen">80%</item>
|
||||
<!-- The platform's desired fixed height for a dialog along the minor axis
|
||||
(the screen is in landscape). This may be either a fraction or a dimension.-->
|
||||
<item type="dimen" name="dialog_fixed_height_minor">100%</item>
|
||||
<item name="dialog_fixed_height_minor" type="dimen">100%</item>
|
||||
|
||||
<!-- Preference activity, vertical padding for the header list -->
|
||||
<dimen name="preference_screen_header_vertical_padding">0dp</dimen>
|
||||
|
||||
<dimen name="preference_screen_header_padding_side">16dip</dimen>
|
||||
<integer name="preference_screen_header_scrollbarStyle">0x02000000</integer> <!-- outsideOverlay -->
|
||||
<integer name="preference_screen_header_scrollbarStyle">0x02000000
|
||||
</integer> <!-- outsideOverlay -->
|
||||
|
||||
<integer name="preference_fragment_scrollbarStyle">0x02000000</integer> <!-- outsideOverlay -->
|
||||
|
||||
|
@ -148,7 +148,7 @@
|
|||
<!-- The platform's desired minimum size for a dialog's width when it
|
||||
is along the minor axis (that is the screen is portrait). This may
|
||||
be either a fraction or a dimension. -->
|
||||
<item type="dimen" name="dialog_min_width_minor">95%</item>
|
||||
<item name="dialog_min_width_minor" type="dimen">95%</item>
|
||||
|
||||
<!-- The width of the big icons in notifications. -->
|
||||
<dimen name="notification_large_icon_width">64dp</dimen>
|
||||
|
@ -358,4 +358,8 @@
|
|||
|
||||
<!-- width of ImmersiveModeConfirmation (-1 for match_parent) -->
|
||||
<dimen name="immersive_mode_cling_width">-1px</dimen>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
<dimen name="fab_margin">16dp</dimen>
|
||||
</resources>
|
||||
|
|
|
@ -156,7 +156,6 @@
|
|||
<string name="pref_proxy_password_dialog">Enter Proxy Password</string>
|
||||
|
||||
|
||||
|
||||
<string name="status">Status</string>
|
||||
<string name="setting_up_full_transparent_proxying_">Setting up full transparent proxying…</string>
|
||||
<string name="setting_up_app_based_transparent_proxying_">Setting up app-based transparent proxying…</string>
|
||||
|
@ -237,7 +236,7 @@
|
|||
<string name="pref_use_expanded_notifications_title">Expanded Notifications</string>
|
||||
|
||||
<string name="notification_using_bridges">Bridges enabled!</string>
|
||||
<string name="default_bridges"/>
|
||||
<string name="default_bridges" />
|
||||
<string name="set_locale_title">Language</string>
|
||||
<string name="set_locale_summary">Choose the locale and language for Orbot</string>
|
||||
<string name="wizard_locale_title">Choose Language</string>
|
||||
|
@ -252,7 +251,7 @@
|
|||
<string name="pref_disable_network_summary">Put Tor to sleep when there is no internet available</string>
|
||||
<string name="newnym">You\'ve switched to a new Tor identity!</string>
|
||||
|
||||
<string name="menu_verify_browser">Browser</string>
|
||||
<string name="menu_verify_browser">Browser</string>
|
||||
<string name="menu_use_chatsecure">Use ChatSecure</string>
|
||||
|
||||
<string name="permission_manage_tor_label">Manage Tor</string>
|
||||
|
@ -263,25 +262,25 @@
|
|||
<string name="network_connectivity_is_good_waking_tor_up_">Network connectivity is good. Waking Tor up…</string>
|
||||
<string name="updating_settings_in_tor_service">updating settings in Tor service</string>
|
||||
|
||||
<string name="pref_socks_title">Tor SOCKS</string>
|
||||
<string name="pref_socks_title">Tor SOCKS</string>
|
||||
<string name="pref_socks_summary">Port that Tor offers its SOCKS proxy on (default: 9050 or 0 to disable)</string>
|
||||
<string name="pref_socks_dialog">SOCKS Port Config</string>
|
||||
|
||||
<string name="pref_transport_title">Tor TransProxy Port</string>
|
||||
<string name="pref_transport_title">Tor TransProxy Port</string>
|
||||
<string name="pref_transport_summary">Port that Tor offers its Transparent Proxy on (default: 9040 or 0 to disable)</string>
|
||||
<string name="pref_transport_dialog">TransProxy Port Config</string>
|
||||
|
||||
|
||||
<string name="pref_dnsport_title">Tor DNS Port</string>
|
||||
<string name="pref_dnsport_title">Tor DNS Port</string>
|
||||
<string name="pref_dnsport_summary">Port that Tor offers its DNS on (default: 5400 or 0 to disable)</string>
|
||||
<string name="pref_dnsport_dialog">DNS Port Config</string>
|
||||
|
||||
|
||||
<string name="pref_torrc_title">Torrc Custom Config</string>
|
||||
<string name="pref_torrc_title">Torrc Custom Config</string>
|
||||
<string name="pref_torrc_summary">EXPERTS ONLY: enter direct torrc config lines</string>
|
||||
<string name="pref_torrc_dialog">Custom Torrc</string>
|
||||
|
||||
<string name="wizard_tips_martus">Mobile Martus - Benetech Human Rights Documentation App</string>
|
||||
<string name="wizard_tips_martus">Mobile Martus - Benetech Human Rights Documentation App</string>
|
||||
<string name="your_tor_public_ips_">Your Tor Public IPs:</string>
|
||||
<string name="please_disable_this_app_in_android_settings_apps_if_you_are_having_problems_with_orbot_">"Please disable this app in Android->Settings->Apps if you are having problems with Orbot: "</string>
|
||||
<string name="app_conflict">App Conflict</string>
|
||||
|
@ -335,5 +334,50 @@
|
|||
|
||||
<string name="note_only_standard_tor_bridges_work_on_intel_x86_atom_devices">NOTE: Only standard Tor bridges work on Intel X86/ATOM devices</string>
|
||||
|
||||
<string name="vpn_default_world">World (Location)</string>
|
||||
<string name="vpn_default_world">World (Location)</string>
|
||||
<string name="hidden_services">Hidden Services</string>
|
||||
<string name="title_activity_hidden_services">Hidden Services</string>
|
||||
<string name="menu_hidden_services">Hidden Services</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="ports">Ports</string>
|
||||
<string name="local_port">Local Port</string>
|
||||
<string name="onion_port">Onion Port</string>
|
||||
<string name="name">Name</string>
|
||||
<string name="done">Done!</string>
|
||||
<string name="invalid_port">Invalid Port</string>
|
||||
<string name="copy_address_to_clipboard">Copy address to clipboard</string>
|
||||
<string name="show_auth_cookie">Show auth cookie</string>
|
||||
<string name="backup_service">Backup Service</string>
|
||||
<string name="delete_service">Delete Service</string>
|
||||
<string name="backup_saved_at_external_storage">Backup saved at external storage</string>
|
||||
<string name="backup_restored">Backup restored</string>
|
||||
<string name="filemanager_not_available">Filemanager not available</string>
|
||||
<string name="please_grant_permissions_for_external_storage">Please grant permissions for external storage</string>
|
||||
<string name="permission_granted">Permission granted</string>
|
||||
<string name="permission_denied">Permission denied</string>
|
||||
<string name="restore_backup">Restore Backup</string>
|
||||
<string name="create_a_backup_first">Create a backup first</string>
|
||||
<string name="name_can_t_be_empty">Name can\'t be empty</string>
|
||||
<string name="fields_can_t_be_empty">Fields can\'t be empty</string>
|
||||
<string name="start_tor_again_for_finish_the_process">Start Tor again for finish the process</string>
|
||||
<string name="confirm_service_deletion">Confirm service deletion</string>
|
||||
<string name="click_again_for_backup">Click again for backup</string>
|
||||
<string name="service_type">Service type</string>
|
||||
<string name="auth_cookie">Auth cookie</string>
|
||||
<string name="copy_cookie_to_clipboard">Copy cookie to clipboard</string>
|
||||
<string name="auth_cookie_was_not_configured">Auth cookie was not configured</string>
|
||||
<string name="please_restart_Orbot_to_enable_the_changes">Please restart Orbot to enable the changes</string>
|
||||
<string name="client_cookies">Client cookies</string>
|
||||
<string name="onion">.onion</string>
|
||||
<string name="invalid_onion_address">Invalid .onion address</string>
|
||||
<string name="cookie_from_QR">Read from QR</string>
|
||||
<string name="backup_cookie">Backup cookie</string>
|
||||
<string name="delete_cookie">Delete cookie</string>
|
||||
<string name="confirm_cookie_deletion">Confirm cookie deletion</string>
|
||||
<string name="hosted_services">Hosted Services</string>
|
||||
<string name="share_as_qr">Share as QR</string>
|
||||
<string name="disable">Disable</string>
|
||||
<string name="enable">Enable</string>
|
||||
<string name="consider_disable_battery_optimizations">Consider disable battery optimizations</string>
|
||||
<string name="consider_enable_battery_optimizations">Consider enable battery optimizations</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="NotificationText" parent="android:TextAppearance.StatusBar.EventContent" />
|
||||
|
||||
<style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title" />
|
||||
|
||||
<style name="DefaultTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
||||
<style name="DefaultTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<files-path name="hidden-services" path="hidden_services/" />
|
||||
</paths>
|
|
@ -157,17 +157,6 @@ android:dialogTitle="@string/enter_ports"
|
|||
/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/pref_hs_group">
|
||||
<CheckBoxPreference android:title="@string/enable_hidden_services"
|
||||
android:summary="@string/run_servers_accessible_via_the_tor_network" android:key="pref_hs_enable"></CheckBoxPreference>
|
||||
<EditTextPreference android:summary="@string/enter_localhost_ports_for_hidden_services"
|
||||
android:title="@string/hidden_service_ports" android:enabled="false" android:key="pref_hs_ports"></EditTextPreference>
|
||||
|
||||
<EditTextPreference android:key="pref_hs_hostname"
|
||||
android:summary="@string/the_addressable_name_for_your_hidden_service_generated_automatically_"
|
||||
android:title=".Onion Hostname"></EditTextPreference>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/pref_proxy_title">
|
||||
<EditTextPreference android:key="pref_proxy_type"
|
||||
android:title="@string/pref_proxy_type_title"
|
||||
|
|
|
@ -16,19 +16,22 @@ import android.app.NotificationManager;
|
|||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.BaseColumns;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.text.TextUtils;
|
||||
|
@ -62,7 +65,6 @@ import java.io.PrintStream;
|
|||
import java.io.PrintWriter;
|
||||
import java.net.Socket;
|
||||
import java.text.Normalizer;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
@ -124,10 +126,54 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
|||
public static File fileObfsclient;
|
||||
public static File fileXtables;
|
||||
public static File fileTorRc;
|
||||
private File mHSBasePath;
|
||||
|
||||
private Shell mShell;
|
||||
private Shell mShellPolipo;
|
||||
|
||||
|
||||
private static final Uri HS_CONTENT_URI = Uri.parse("content://org.torproject.android.ui.hiddenservices.providers/hs");
|
||||
private static final Uri COOKIE_CONTENT_URI = Uri.parse("content://org.torproject.android.ui.hiddenservices.providers.cookie/cookie");
|
||||
|
||||
public static final class HiddenService 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() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ClientCookie implements BaseColumns {
|
||||
public static final String DOMAIN = "domain";
|
||||
public static final String AUTH_COOKIE_VALUE = "auth_cookie_value";
|
||||
public static final String ENABLED = "enabled";
|
||||
|
||||
private ClientCookie() {
|
||||
}
|
||||
}
|
||||
|
||||
private 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 String[] cookieProjection = new String[]{
|
||||
ClientCookie._ID,
|
||||
ClientCookie.DOMAIN,
|
||||
ClientCookie.AUTH_COOKIE_VALUE,
|
||||
ClientCookie.ENABLED};
|
||||
|
||||
public void debug(String msg)
|
||||
{
|
||||
if (Prefs.useDebugLogging())
|
||||
|
@ -441,75 +487,6 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
|||
sendCallbackStatus(STATUS_OFF);
|
||||
}
|
||||
|
||||
|
||||
private String getHiddenServiceHostname ()
|
||||
{
|
||||
|
||||
SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext());
|
||||
|
||||
boolean enableHiddenServices = prefs.getBoolean("pref_hs_enable", false);
|
||||
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
if (enableHiddenServices)
|
||||
{
|
||||
String hsPorts = prefs.getString("pref_hs_ports","");
|
||||
|
||||
StringTokenizer st = new StringTokenizer (hsPorts,",");
|
||||
String hsPortConfig = null;
|
||||
|
||||
while (st.hasMoreTokens())
|
||||
{
|
||||
|
||||
int hsPort = Integer.parseInt(st.nextToken().split(" ")[0]);;
|
||||
|
||||
File fileDir = new File(appCacheHome, "hs" + hsPort);
|
||||
File file = new File(fileDir, "hostname");
|
||||
|
||||
|
||||
if (file.exists())
|
||||
{
|
||||
try {
|
||||
String onionHostname = Utils.readString(new FileInputStream(file)).trim();
|
||||
|
||||
if (result.length() > 0)
|
||||
result.append(",");
|
||||
|
||||
result.append(onionHostname);
|
||||
|
||||
|
||||
} 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);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
showToolbarNotification(getString(R.string.unable_to_read_hidden_service_name), HS_NOTIFY_ID, R.drawable.ic_stat_notifyerr);
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (result.length() > 0)
|
||||
{
|
||||
String onionHostname = result.toString();
|
||||
|
||||
showToolbarNotification(getString(R.string.hidden_service_on) + ' ' + onionHostname, HS_NOTIFY_ID, R.drawable.ic_stat_tor);
|
||||
Editor pEdit = prefs.edit();
|
||||
pEdit.putString("pref_hs_hostname",onionHostname);
|
||||
pEdit.commit();
|
||||
|
||||
return onionHostname;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private void killAllDaemons() throws Exception {
|
||||
if (conn != null) {
|
||||
logNotice("Using control port to shutdown Tor");
|
||||
|
@ -577,6 +554,14 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
|||
fileXtables = new File(appBinHome, TorServiceConstants.IPTABLES_ASSET_KEY);
|
||||
fileTorRc = new File(appBinHome, TorServiceConstants.TORRC_ASSET_KEY);
|
||||
|
||||
mHSBasePath = new File(
|
||||
getFilesDir().getAbsolutePath(),
|
||||
TorServiceConstants.HIDDEN_SERVICES_DIR
|
||||
);
|
||||
|
||||
if (!mHSBasePath.isDirectory())
|
||||
mHSBasePath.mkdirs();
|
||||
|
||||
mEventHandler = new TorEventHandler(this);
|
||||
|
||||
if (mNotificationManager == null)
|
||||
|
@ -809,7 +794,56 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
|||
enableTransparentProxy();
|
||||
}
|
||||
|
||||
getHiddenServiceHostname ();
|
||||
// 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);
|
||||
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));
|
||||
|
||||
// Update only new domains or restored from backup with auth cookie
|
||||
if((HSDomain == null || HSDomain.length() < 1) || (HSAuthCookie == 1 && (HSAuthCookieValue == null || HSAuthCookieValue.length() < 1))) {
|
||||
String hsDirPath = new File(mHSBasePath.getAbsolutePath(),"hs" + HSLocalPort).getCanonicalPath();
|
||||
File file = new File(hsDirPath, "hostname");
|
||||
|
||||
if (file.exists())
|
||||
{
|
||||
ContentValues fields = new ContentValues();
|
||||
|
||||
try {
|
||||
String onionHostname = Utils.readString(new FileInputStream(file)).trim();
|
||||
if(HSAuthCookie == 1) {
|
||||
String[] aux = onionHostname.split(" ");
|
||||
onionHostname = aux[0];
|
||||
fields.put(HiddenService.AUTH_COOKIE_VALUE, aux[1]);
|
||||
}
|
||||
fields.put(HiddenService.DOMAIN, onionHostname);
|
||||
mCR.update(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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
showToolbarNotification(getString(R.string.unable_to_read_hidden_service_name), HS_NOTIFY_ID, R.drawable.ic_stat_notifyerr);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(OrbotConstants.TAG,"error parsing hsport",e);
|
||||
} catch (Exception e) {
|
||||
Log.e(OrbotConstants.TAG,"error starting share server",e);
|
||||
}
|
||||
|
||||
hidden_services.close();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logException("Unable to start Tor: " + e.toString(), e);
|
||||
|
@ -1574,7 +1608,6 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
|||
|
||||
boolean becomeRelay = prefs.getBoolean(OrbotConstants.PREF_OR, false);
|
||||
boolean ReachableAddresses = prefs.getBoolean(OrbotConstants.PREF_REACHABLE_ADDRESSES,false);
|
||||
boolean enableHiddenServices = prefs.getBoolean("pref_hs_enable", false);
|
||||
|
||||
boolean enableStrictNodes = prefs.getBoolean("pref_strict_nodes", false);
|
||||
String entranceNodes = prefs.getString("pref_entrance_nodes", "");
|
||||
|
@ -1777,48 +1810,51 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
|||
return false;
|
||||
}
|
||||
|
||||
if (enableHiddenServices)
|
||||
{
|
||||
logNotice("hidden services are enabled");
|
||||
ContentResolver mCR = getApplicationContext().getContentResolver();
|
||||
|
||||
//updateConfiguration("RendPostPeriod", "600 seconds", false); //possible feature to investigate
|
||||
/* ---- Hidden Services ---- */
|
||||
Cursor hidden_services = mCR.query(HS_CONTENT_URI, hsProjection, HiddenService.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 hsDirPath = new File(mHSBasePath.getAbsolutePath(),"hs" + HSLocalPort).getCanonicalPath();
|
||||
|
||||
String hsPorts = prefs.getString("pref_hs_ports","");
|
||||
|
||||
StringTokenizer st = new StringTokenizer (hsPorts,",");
|
||||
String hsPortConfig = null;
|
||||
int hsPort = -1;
|
||||
|
||||
while (st.hasMoreTokens())
|
||||
{
|
||||
try
|
||||
{
|
||||
hsPortConfig = st.nextToken().trim();
|
||||
|
||||
if (hsPortConfig.indexOf(":")==-1) //setup the port to localhost if not specifed
|
||||
{
|
||||
hsPortConfig = hsPortConfig + " 127.0.0.1:" + hsPortConfig;
|
||||
}
|
||||
|
||||
hsPort = Integer.parseInt(hsPortConfig.split(" ")[0]);
|
||||
|
||||
String hsDirPath = new File(appCacheHome,"hs" + hsPort).getCanonicalPath();
|
||||
|
||||
debug("Adding hidden service on port: " + hsPortConfig);
|
||||
debug("Adding hidden service on port: " + HSLocalPort);
|
||||
|
||||
extraLines.append("HiddenServiceDir" + ' ' + hsDirPath).append('\n');
|
||||
extraLines.append("HiddenServicePort" + ' ' + hsPortConfig).append('\n');
|
||||
extraLines.append("HiddenServicePort" + ' ' + HSOnionPort + " 127.0.0.1:" + HSLocalPort).append('\n');
|
||||
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(OrbotConstants.TAG,"error parsing hsport",e);
|
||||
} catch (Exception e) {
|
||||
Log.e(OrbotConstants.TAG,"error starting share server",e);
|
||||
if(HSAuthCookie == 1)
|
||||
extraLines.append("HiddenServiceAuthorizeClient stealth " + HSname).append('\n');
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(OrbotConstants.TAG,"error parsing hsport",e);
|
||||
} catch (Exception e) {
|
||||
Log.e(OrbotConstants.TAG,"error starting share server",e);
|
||||
}
|
||||
|
||||
hidden_services.close();
|
||||
}
|
||||
|
||||
}
|
||||
/* ---- Client Cookies ---- */
|
||||
Cursor client_cookies = mCR.query(COOKIE_CONTENT_URI, cookieProjection, 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');
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(OrbotConstants.TAG,"error starting share server",e);
|
||||
}
|
||||
|
||||
client_cookies.close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -161,4 +161,6 @@ public interface TorServiceConstants {
|
|||
"meek_lite 0.0.2.0:3 url=https://az668014.vo.msecnd.net/ front=ajax.aspnetcdn.com"
|
||||
};
|
||||
|
||||
public static final String HIDDEN_SERVICES_DIR = "hidden_services";
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue