New feature added: HidServAuth manager and QR share
This commit is contained in:
parent
9961ad0b84
commit
2aa2b4c370
|
@ -1,11 +1,13 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.torproject.android"
|
package="org.torproject.android"
|
||||||
android:versionName="15.2.0-RC-8-multi"
|
android:installLocation="auto"
|
||||||
android:versionCode="15208000"
|
android:versionCode="15208000"
|
||||||
android:installLocation="auto"
|
android:versionName="15.2.0-RC-8-multi">
|
||||||
>
|
|
||||||
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="23"/>
|
<uses-sdk
|
||||||
|
android:minSdkVersion="16"
|
||||||
|
android:targetSdkVersion="23" />
|
||||||
<!--
|
<!--
|
||||||
<permission android:name="org.torproject.android.MANAGE_TOR"
|
<permission android:name="org.torproject.android.MANAGE_TOR"
|
||||||
android:label="@string/permission_manage_tor_label"
|
android:label="@string/permission_manage_tor_label"
|
||||||
|
@ -13,16 +15,17 @@
|
||||||
android:protectionLevel="signature"/>
|
android:protectionLevel="signature"/>
|
||||||
|
|
||||||
<uses-permission android:name="org.torproject.android.MANAGE_TOR"/>
|
<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.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
|
<android:uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".OrbotApp"
|
android:name=".OrbotApp"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
|
@ -38,118 +41,114 @@
|
||||||
android:name=".OrbotMainActivity"
|
android:name=".OrbotMainActivity"
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop">
|
||||||
>
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<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>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
<data android:scheme="bridge" />
|
<data android:scheme="bridge" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
<intent-filter>
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
|
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<action android:name="org.torproject.android.START_TOR" />
|
<action android:name="org.torproject.android.START_TOR" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<!--
|
<!-- This is for ensuring the background service still runs when/if the app is swiped away -->
|
||||||
This is for ensuring the background service still runs when/if the app is swiped away
|
<activity
|
||||||
-->
|
android:name=".service.util.DummyActivity"
|
||||||
<activity
|
android:allowTaskReparenting="true"
|
||||||
android:name=".service.util.DummyActivity"
|
android:alwaysRetainTaskState="false"
|
||||||
android:theme="@android:style/Theme.Translucent"
|
android:clearTaskOnLaunch="true"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:allowTaskReparenting="true"
|
android:excludeFromRecents="true"
|
||||||
android:noHistory="true"
|
android:finishOnTaskLaunch="true"
|
||||||
android:excludeFromRecents="true"
|
android:noHistory="true"
|
||||||
android:alwaysRetainTaskState="false"
|
android:stateNotNeeded="true"
|
||||||
android:stateNotNeeded="true"
|
android:theme="@android:style/Theme.Translucent" />
|
||||||
android:clearTaskOnLaunch="true"
|
<activity
|
||||||
android:finishOnTaskLaunch="true"
|
android:name=".vpn.VPNEnableActivity"
|
||||||
|
android:exported="false"
|
||||||
/>
|
android:label="@string/app_name" />
|
||||||
|
<activity
|
||||||
<activity
|
android:name=".ui.PromoAppsActivity"
|
||||||
android:name=".vpn.VPNEnableActivity" android:label="@string/app_name" android:exported="false"
|
android:exported="false" />
|
||||||
/>
|
<activity
|
||||||
|
android:name=".settings.SettingsPreferences"
|
||||||
|
android:label="@string/app_name" />
|
||||||
<activity android:name="org.torproject.android.ui.PromoAppsActivity" android:exported="false"/>
|
<activity
|
||||||
|
android:name=".ui.AppManager"
|
||||||
|
android:label="@string/app_name"
|
||||||
<activity android:name=".settings.SettingsPreferences" android:label="@string/app_name"/>
|
android:theme="@style/Theme.AppCompat" />
|
||||||
<activity android:name=".ui.AppManager" android:label="@string/app_name"
|
|
||||||
android:theme="@style/Theme.AppCompat"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.TorService"
|
android:name=".service.TorService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
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>
|
||||||
|
|
||||||
|
|
||||||
<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
|
<receiver
|
||||||
android:name=".service.StartTorReceiver"
|
android:name=".service.StartTorReceiver"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.torproject.android.intent.action.START" />
|
<action android:name="org.torproject.android.intent.action.START" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
<receiver
|
||||||
|
android:name=".OnBootReceiver"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
|
||||||
<receiver android:name=".OnBootReceiver"
|
<category android:name="android.intent.category.HOME" />
|
||||||
android:enabled="true" android:exported="true"
|
</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.BOOT_COMPLETED" />
|
<intent-filter>
|
||||||
<category android:name="android.intent.category.HOME" />
|
<action android:name="android.intent.action.MEDIA_MOUNTED" />
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
<category android:name="android.intent.category.HOME" />
|
||||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
</intent-filter>
|
||||||
<category android:name="android.intent.category.HOME" />
|
</receiver>
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
|
|
||||||
<category android:name="android.intent.category.HOME" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.hiddenservices.HiddenServicesActivity"
|
android:name=".ui.hiddenservices.HiddenServicesActivity"
|
||||||
android:label="@string/title_activity_hidden_services"
|
android:label="@string/title_activity_hidden_services"
|
||||||
android:theme="@style/DefaultTheme" >
|
android:theme="@style/DefaultTheme">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="org.torproject.android.OrbotMainActivity" />
|
android:value=".OrbotMainActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".ui.hiddenservices.providers.HSContentProvider"
|
android:name=".ui.hiddenservices.providers.HSContentProvider"
|
||||||
android:exported="false"
|
android:authorities="org.torproject.android.ui.hiddenservices.providers"
|
||||||
android:authorities="org.torproject.android.ui.hiddenservices.providers" />
|
android:exported="false" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="android.support.v4.content.FileProvider"
|
android:name="android.support.v4.content.FileProvider"
|
||||||
android:authorities="org.torproject.android.ui.hiddenservices.storage"
|
android:authorities="org.torproject.android.ui.hiddenservices.storage"
|
||||||
|
@ -157,8 +156,22 @@
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/hidden_services_paths"/>
|
android:resource="@xml/hidden_services_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
</application>
|
<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>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
|
@ -27,6 +27,7 @@ import org.torproject.android.ui.AppManager;
|
||||||
import org.torproject.android.ui.ImageProgressView;
|
import org.torproject.android.ui.ImageProgressView;
|
||||||
import org.torproject.android.ui.PromoAppsActivity;
|
import org.torproject.android.ui.PromoAppsActivity;
|
||||||
import org.torproject.android.ui.Rotate3dAnimation;
|
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.HiddenServicesActivity;
|
||||||
import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
|
import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
|
||||||
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
||||||
|
@ -513,6 +514,8 @@ public class OrbotMainActivity extends AppCompatActivity
|
||||||
|
|
||||||
} else if (item.getItemId() == R.id.menu_hidden_services) {
|
} else if (item.getItemId() == R.id.menu_hidden_services) {
|
||||||
startActivity(new Intent(this, HiddenServicesActivity.class));
|
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);
|
return super.onOptionsItemSelected(item);
|
||||||
|
|
|
@ -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.ClienCookiesAdapter;
|
||||||
|
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.providers.CookieContentProvider;
|
||||||
|
import org.torproject.android.ui.hiddenservices.storage.PermissionManager;
|
||||||
|
|
||||||
|
public class ClientCookiesActivity extends AppCompatActivity {
|
||||||
|
public final int WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTIONBAR = 3;
|
||||||
|
|
||||||
|
private ContentResolver mResolver;
|
||||||
|
private ClienCookiesAdapter 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 ClienCookiesAdapter(
|
||||||
|
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.usesRuntimePermissions()
|
||||||
|
&& !PermissionManager.hasExternalWritePermission(this)) {
|
||||||
|
PermissionManager.requestPermissions(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
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -24,7 +24,7 @@ import org.torproject.android.R;
|
||||||
import org.torproject.android.ui.hiddenservices.adapters.OnionListAdapter;
|
import org.torproject.android.ui.hiddenservices.adapters.OnionListAdapter;
|
||||||
import org.torproject.android.ui.hiddenservices.dialogs.HSActionsDialog;
|
import org.torproject.android.ui.hiddenservices.dialogs.HSActionsDialog;
|
||||||
import org.torproject.android.ui.hiddenservices.dialogs.HSDataDialog;
|
import org.torproject.android.ui.hiddenservices.dialogs.HSDataDialog;
|
||||||
import org.torproject.android.ui.hiddenservices.dialogs.SelectBackupDialog;
|
import org.torproject.android.ui.hiddenservices.dialogs.SelectHSBackupDialog;
|
||||||
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
||||||
import org.torproject.android.ui.hiddenservices.storage.PermissionManager;
|
import org.torproject.android.ui.hiddenservices.storage.PermissionManager;
|
||||||
|
|
||||||
|
@ -156,9 +156,8 @@ public class HiddenServicesActivity extends AppCompatActivity {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectBackupDialog dialog = new SelectBackupDialog();
|
SelectHSBackupDialog dialog = new SelectHSBackupDialog();
|
||||||
dialog.show(getSupportFragmentManager(), "SelectBackupDialog");
|
dialog.show(getSupportFragmentManager(), "SelectHSBackupDialog");
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
|
@ -174,8 +173,8 @@ public class HiddenServicesActivity extends AppCompatActivity {
|
||||||
|
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
case WRITE_EXTERNAL_STORAGE_FROM_ACTIONBAR: {
|
case WRITE_EXTERNAL_STORAGE_FROM_ACTIONBAR: {
|
||||||
SelectBackupDialog dialog = new SelectBackupDialog();
|
SelectHSBackupDialog dialog = new SelectHSBackupDialog();
|
||||||
dialog.show(getSupportFragmentManager(), "SelectBackupDialog");
|
dialog.show(getSupportFragmentManager(), "SelectHSBackupDialog");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case HSActionsDialog.WRITE_EXTERNAL_STORAGE_FROM_ACTION_DIALOG: {
|
case HSActionsDialog.WRITE_EXTERNAL_STORAGE_FROM_ACTION_DIALOG: {
|
||||||
|
|
|
@ -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 ClienCookiesAdapter extends CursorAdapter {
|
||||||
|
private LayoutInflater cursorInflater;
|
||||||
|
|
||||||
|
public ClienCookiesAdapter(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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.torproject.android.R;
|
import org.torproject.android.R;
|
||||||
import org.torproject.android.service.TorServiceConstants;
|
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.providers.HSContentProvider;
|
||||||
import org.torproject.android.ui.hiddenservices.storage.ExternalStorage;
|
import org.torproject.android.ui.hiddenservices.storage.ExternalStorage;
|
||||||
|
|
||||||
|
@ -28,21 +29,19 @@ import java.nio.charset.Charset;
|
||||||
|
|
||||||
public class BackupUtils {
|
public class BackupUtils {
|
||||||
private final String configFileName = "config.json";
|
private final String configFileName = "config.json";
|
||||||
private File mHSBasePath;
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private ContentResolver mResolver;
|
private ContentResolver mResolver;
|
||||||
|
|
||||||
public BackupUtils(Context context) {
|
public BackupUtils(Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mHSBasePath = new File(
|
|
||||||
mContext.getFilesDir().getAbsolutePath(),
|
|
||||||
TorServiceConstants.HIDDEN_SERVICES_DIR
|
|
||||||
);
|
|
||||||
|
|
||||||
mResolver = mContext.getContentResolver();
|
mResolver = mContext.getContentResolver();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String createZipBackup(Integer port) {
|
public String createZipBackup(Integer port) {
|
||||||
|
File mHSBasePath = new File(
|
||||||
|
mContext.getFilesDir().getAbsolutePath(),
|
||||||
|
TorServiceConstants.HIDDEN_SERVICES_DIR
|
||||||
|
);
|
||||||
|
|
||||||
String configFilePath = mHSBasePath + "/hs" + port + "/" + configFileName;
|
String configFilePath = mHSBasePath + "/hs" + port + "/" + configFileName;
|
||||||
String hostnameFilePath = mHSBasePath + "/hs" + port + "/hostname";
|
String hostnameFilePath = mHSBasePath + "/hs" + port + "/hostname";
|
||||||
|
@ -115,6 +114,8 @@ public class BackupUtils {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
portData.close();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
FileWriter file = new FileWriter(configFilePath);
|
FileWriter file = new FileWriter(configFilePath);
|
||||||
file.write(config.toString());
|
file.write(config.toString());
|
||||||
|
@ -124,8 +125,6 @@ public class BackupUtils {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
portData.close();
|
|
||||||
|
|
||||||
String zip_path = storage_path.getAbsolutePath() + "/hs" + port + ".zip";
|
String zip_path = storage_path.getAbsolutePath() + "/hs" + port + ".zip";
|
||||||
String files[] = {hostnameFilePath, keyFilePath, configFilePath};
|
String files[] = {hostnameFilePath, keyFilePath, configFilePath};
|
||||||
|
|
||||||
|
@ -139,6 +138,11 @@ public class BackupUtils {
|
||||||
|
|
||||||
public void restoreZipBackup(File backup) {
|
public void restoreZipBackup(File backup) {
|
||||||
|
|
||||||
|
File mHSBasePath = new File(
|
||||||
|
mContext.getFilesDir().getAbsolutePath(),
|
||||||
|
TorServiceConstants.HIDDEN_SERVICES_DIR
|
||||||
|
);
|
||||||
|
|
||||||
int port;
|
int port;
|
||||||
Cursor service;
|
Cursor service;
|
||||||
String backupName = backup.getName();
|
String backupName = backup.getName();
|
||||||
|
@ -154,7 +158,7 @@ public class BackupUtils {
|
||||||
zip.unzip(hsPath.getAbsolutePath());
|
zip.unzip(hsPath.getAbsolutePath());
|
||||||
|
|
||||||
File config = new File(configFilePath);
|
File config = new File(configFilePath);
|
||||||
FileInputStream stream = null;
|
FileInputStream stream;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
stream = new FileInputStream(config);
|
stream = new FileInputStream(config);
|
||||||
|
@ -236,6 +240,10 @@ public class BackupUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void restoreKeyBackup(int hsPort, Uri hsKeyPath) {
|
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);
|
File serviceDir = new File(mHSBasePath, "hs" + hsPort);
|
||||||
|
|
||||||
|
@ -258,4 +266,71 @@ public class BackupUtils {
|
||||||
e.printStackTrace();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.database.sqlite.SQLiteOpenHelper;
|
||||||
public class HSDatabase extends SQLiteOpenHelper {
|
public class HSDatabase extends SQLiteOpenHelper {
|
||||||
|
|
||||||
public static final String HS_DATA_TABLE_NAME = "hs_data";
|
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 int DATABASE_VERSION = 2;
|
||||||
private static final String DATABASE_NAME = "hidden_services";
|
private static final String DATABASE_NAME = "hidden_services";
|
||||||
private static final String HS_DATA_TABLE_CREATE =
|
private static final String HS_DATA_TABLE_CREATE =
|
||||||
|
@ -22,6 +23,13 @@ public class HSDatabase extends SQLiteOpenHelper {
|
||||||
"enabled INTEGER DEFAULT 1, " +
|
"enabled INTEGER DEFAULT 1, " +
|
||||||
"port INTEGER);";
|
"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) {
|
public HSDatabase(Context context) {
|
||||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||||
}
|
}
|
||||||
|
@ -29,6 +37,7 @@ public class HSDatabase extends SQLiteOpenHelper {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(SQLiteDatabase db) {
|
public void onCreate(SQLiteDatabase db) {
|
||||||
db.execSQL(HS_DATA_TABLE_CREATE);
|
db.execSQL(HS_DATA_TABLE_CREATE);
|
||||||
|
db.execSQL(HS_CLIENT_COOKIE_TABLE_CREATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -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.storage.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.usesRuntimePermissions()
|
||||||
|
&& !PermissionManager.hasExternalWritePermission(mContext)) {
|
||||||
|
|
||||||
|
PermissionManager.requestPermissions(
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,12 @@ import android.widget.Button;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
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.R;
|
||||||
|
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
||||||
|
|
||||||
public class HSCookieDialog extends DialogFragment {
|
public class HSCookieDialog extends DialogFragment {
|
||||||
|
|
||||||
|
@ -44,6 +49,26 @@ public class HSCookieDialog extends DialogFragment {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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);
|
Button cancel = (Button) dialog_view.findViewById(R.id.hs_cookie_cancel);
|
||||||
cancel.setOnClickListener(new View.OnClickListener() {
|
cancel.setOnClickListener(new View.OnClickListener() {
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class SelectBackupDialog extends DialogFragment {
|
public class SelectHSBackupDialog extends DialogFragment {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
|
@ -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() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,13 +5,13 @@ import android.os.Environment;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
public class ExternalStorage {
|
public class ExternalStorage {
|
||||||
private static final String BACKUPS_DIR = "Orbot-HiddenServices";
|
private static final String ORBOT_BACKUPS_DIR = "Orbot";
|
||||||
|
|
||||||
public static File getOrCreateBackupDir() {
|
public static File getOrCreateBackupDir() {
|
||||||
if (!isExternalStorageWritable())
|
if (!isExternalStorageWritable())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
File dir = new File(Environment.getExternalStorageDirectory(), BACKUPS_DIR);
|
File dir = new File(Environment.getExternalStorageDirectory(), ORBOT_BACKUPS_DIR);
|
||||||
|
|
||||||
if (!dir.isDirectory() && !dir.mkdirs())
|
if (!dir.isDirectory() && !dir.mkdirs())
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -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>
|
|
@ -17,6 +17,12 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:id="@+id/hs_cookie_to_clipboard" />
|
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
|
<Button
|
||||||
android:text="@string/btn_cancel"
|
android:text="@string/btn_cancel"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -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>
|
|
@ -44,10 +44,22 @@
|
||||||
</menu>
|
</menu>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item android:id="@+id/menu_hidden_services"
|
<item
|
||||||
android:title="@string/menu_hidden_services"
|
android:title="@string/menu_hidden_services"
|
||||||
yourapp:showAsAction="never"
|
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"
|
<item android:id="@+id/menu_promo_apps"
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<resources></resources>
|
|
@ -156,7 +156,6 @@
|
||||||
<string name="pref_proxy_password_dialog">Enter Proxy Password</string>
|
<string name="pref_proxy_password_dialog">Enter Proxy Password</string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<string name="status">Status</string>
|
<string name="status">Status</string>
|
||||||
<string name="setting_up_full_transparent_proxying_">Setting up full transparent proxying…</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>
|
<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="pref_use_expanded_notifications_title">Expanded Notifications</string>
|
||||||
|
|
||||||
<string name="notification_using_bridges">Bridges enabled!</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_title">Language</string>
|
||||||
<string name="set_locale_summary">Choose the locale and language for Orbot</string>
|
<string name="set_locale_summary">Choose the locale and language for Orbot</string>
|
||||||
<string name="wizard_locale_title">Choose Language</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="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="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="menu_use_chatsecure">Use ChatSecure</string>
|
||||||
|
|
||||||
<string name="permission_manage_tor_label">Manage Tor</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="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="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_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_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_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_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_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_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_summary">EXPERTS ONLY: enter direct torrc config lines</string>
|
||||||
<string name="pref_torrc_dialog">Custom Torrc</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="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="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>
|
<string name="app_conflict">App Conflict</string>
|
||||||
|
@ -359,6 +358,7 @@
|
||||||
<string name="restore_backup">Restore Backup</string>
|
<string name="restore_backup">Restore Backup</string>
|
||||||
<string name="create_a_backup_first">Create a backup first</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="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="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="confirm_service_deletion">Confirm service deletion</string>
|
||||||
<string name="click_again_for_backup">Click again for backup</string>
|
<string name="click_again_for_backup">Click again for backup</string>
|
||||||
|
@ -367,4 +367,13 @@
|
||||||
<string name="copy_cookie_to_clipboard">Copy cookie to clipboard</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="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="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>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -132,7 +132,8 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
||||||
private Shell mShellPolipo;
|
private Shell mShellPolipo;
|
||||||
|
|
||||||
|
|
||||||
private static final Uri CONTENT_URI = Uri.parse("content://org.torproject.android.ui.hiddenservices.providers/hs");
|
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 class HiddenService implements BaseColumns {
|
||||||
public static final String NAME = "name";
|
public static final String NAME = "name";
|
||||||
|
@ -148,7 +149,16 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String[] mProjection = new String[]{
|
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._ID,
|
||||||
HiddenService.NAME,
|
HiddenService.NAME,
|
||||||
HiddenService.DOMAIN,
|
HiddenService.DOMAIN,
|
||||||
|
@ -158,6 +168,12 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
||||||
HiddenService.ONION_PORT,
|
HiddenService.ONION_PORT,
|
||||||
HiddenService.ENABLED};
|
HiddenService.ENABLED};
|
||||||
|
|
||||||
|
private String[] cookieProjection = new String[]{
|
||||||
|
ClientCookie._ID,
|
||||||
|
ClientCookie.DOMAIN,
|
||||||
|
ClientCookie.AUTH_COOKIE_VALUE,
|
||||||
|
ClientCookie.ENABLED};
|
||||||
|
|
||||||
public void debug(String msg)
|
public void debug(String msg)
|
||||||
{
|
{
|
||||||
if (Prefs.useDebugLogging())
|
if (Prefs.useDebugLogging())
|
||||||
|
@ -778,7 +794,7 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
||||||
|
|
||||||
// Tor is running, update new .onion names at db
|
// Tor is running, update new .onion names at db
|
||||||
ContentResolver mCR = getApplicationContext().getContentResolver();
|
ContentResolver mCR = getApplicationContext().getContentResolver();
|
||||||
Cursor hidden_services = mCR.query(CONTENT_URI, mProjection, null, null, null);
|
Cursor hidden_services = mCR.query(HS_CONTENT_URI, hsProjection, null, null, null);
|
||||||
if(hidden_services != null) {
|
if(hidden_services != null) {
|
||||||
try {
|
try {
|
||||||
while (hidden_services.moveToNext()) {
|
while (hidden_services.moveToNext()) {
|
||||||
|
@ -804,7 +820,7 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
||||||
fields.put(HiddenService.AUTH_COOKIE_VALUE, aux[1]);
|
fields.put(HiddenService.AUTH_COOKIE_VALUE, aux[1]);
|
||||||
}
|
}
|
||||||
fields.put(HiddenService.DOMAIN, onionHostname);
|
fields.put(HiddenService.DOMAIN, onionHostname);
|
||||||
mCR.update(CONTENT_URI, fields, "port=" + HSLocalPort , null);
|
mCR.update(HS_CONTENT_URI, fields, "port=" + HSLocalPort , null);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
logException("unable to read onion hostname file",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);
|
showToolbarNotification(getString(R.string.unable_to_read_hidden_service_name), HS_NOTIFY_ID, R.drawable.ic_stat_notifyerr);
|
||||||
|
@ -1792,9 +1808,10 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Hidden Services ---- */
|
|
||||||
ContentResolver mCR = getApplicationContext().getContentResolver();
|
ContentResolver mCR = getApplicationContext().getContentResolver();
|
||||||
Cursor hidden_services = mCR.query(CONTENT_URI, mProjection, HiddenService.ENABLED + "=1", null, null);
|
|
||||||
|
/* ---- Hidden Services ---- */
|
||||||
|
Cursor hidden_services = mCR.query(HS_CONTENT_URI, hsProjection, HiddenService.ENABLED + "=1", null, null);
|
||||||
if(hidden_services != null) {
|
if(hidden_services != null) {
|
||||||
try {
|
try {
|
||||||
while (hidden_services.moveToNext()) {
|
while (hidden_services.moveToNext()) {
|
||||||
|
@ -1821,6 +1838,22 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
||||||
hidden_services.close();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue