Orbot 0.0.2: major UI improvements w/ editable settings and more

svn:r21042
This commit is contained in:
Nathan Freitas 2009-11-28 15:41:28 +00:00
parent e581f4e8b6
commit 084d69fb40
26 changed files with 590 additions and 255 deletions

View File

@ -3,6 +3,7 @@
<classpathentry kind="src" path="src"/> <classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/> <classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/> <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="lib" path="jsocks.jar"/> <classpathentry kind="lib" path="libs/jsocks.jar"/>
<classpathentry combineaccessrules="false" kind="src" path="/jtorctrl"/>
<classpathentry kind="output" path="bin"/> <classpathentry kind="output" path="bin"/>
</classpath> </classpath>

10
BUILD
View File

@ -91,6 +91,16 @@ We need to build our Java SOCKS library:
ant jar ant jar
cp bin/jar/asocks.jar ../Orbot/libs cp bin/jar/asocks.jar ../Orbot/libs
We need to get the TorControl library for Java (https://svn.torproject.org/svn/torctl/trunk/doc/howto.txt):
git clone git://git.torproject.org/git/jtorctl
cd jtorctl
mkdir bin
javac net/freehaven/tor/control/TorControlConnection.java -d bin
cd bin
jar cvf jtorctrl.jar *
cp jtorctrl.jar {Orbot Home}/libs
Finally, we'll make a proper Android package with ant and the Android App SDK: Finally, we'll make a proper Android package with ant and the Android App SDK:
export APP_SDK=~/Documents/projects/android/android-sdk-linux_x86-1.5_r3/tools export APP_SDK=~/Documents/projects/android/android-sdk-linux_x86-1.5_r3/tools

View File

@ -1,6 +1,12 @@
0.0.2 - 2009-11-27
- Major improvement to the user interface including relative layout for different screens
- New graphics resources
- Implemented Tor Control Port using official Java library
- Added 'clear' button to the log screen
- Added 'save' and editable textbox for the settings screen
- Moved screen navigation to a pop-up menu
Changes in version 0.0.1-alpha - 2009-10-21 Changes in version 0.0.1-alpha - 2009-10-21
- First code release - First code release
- Major Features: Tor binary application is fully operational, UI: Start/Stop Tor, View Message Log, View Settings (torrc) - Major Features: Tor binary application is fully operational, UI: Start/Stop Tor, View Message Log, View Settings (torrc)

View File

@ -52,11 +52,11 @@ DataDirectory /data/data/org.torproject.android/data
## The port on which Tor will listen for local connections from Tor ## The port on which Tor will listen for local connections from Tor
## controller applications, as documented in control-spec.txt. ## controller applications, as documented in control-spec.txt.
#ControlPort 9051 ControlPort 9051
## If you enable the controlport, be sure to enable one of these ## If you enable the controlport, be sure to enable one of these
## authentication methods, to prevent attackers from accessing it. ## authentication methods, to prevent attackers from accessing it.
#HashedControlPassword 16:872860B76453A77D60CA2BB8C1A7042072093276A3D701AD684053EC4C #HashedControlPassword 16:872860B76453A77D60CA2BB8C1A7042072093276A3D701AD684053EC4C
#CookieAuthentication 1 CookieAuthentication 1
############### This section is just for location-hidden services ### ############### This section is just for location-hidden services ###

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project name="Orbot" default="help"> <project name="Orbot">
<!-- The local.properties file is created and updated by the 'android' tool. <!-- The local.properties file is created and updated by the 'android' tool.
It contain the path to the SDK. It should *NOT* be checked in in Version It contain the path to the SDK. It should *NOT* be checked in in Version

View File

@ -7,16 +7,8 @@
# "build.properties", and override values to adapt the script to your # "build.properties", and override values to adapt the script to your
# project structure. # project structure.
# apk configurations. This property allows creation of APK files with limited
# resources. For example, if your application contains many locales and
# you wish to release multiple smaller apks instead of a large one, you can
# define configuration to create apks with limited language sets.
# Format is a comma separated list of configuration names. For each
# configuration, a property will declare the resource configurations to
# include. Example:
# apk-configurations=european,northamerica
# apk-config-european=en,fr,it,de,es
# apk-config-northamerica=en,es
apk-configurations= apk-configurations=
# Project target. # Project target.
target=android-2 target=android-4
# Indicates whether an apk should be generated for each density.
split.density=false

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
res/drawable/ic_menu_register.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
res/drawable/vidalia.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -16,13 +16,11 @@
</ScrollView> </ScrollView>
<Button android:id="@+id/btnLogClose"
android:layout_width="fill_parent" <Button android:id="@+id/btnLogClear"
android:layout_height="40px" android:layout_height="40px"
android:text="Close Log" android:text="Clear Log"
android:layout_margin="0sp" android:layout_margin="0sp"
android:layout_width="fill_parent"/>
/>
</LinearLayout> </LinearLayout>

View File

@ -1,63 +1,48 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget29" android:orientation="vertical"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android" android:background="@drawable/bgdarkdroid">
android:background="#cccccc"
> <ScrollView android:id="@+id/mainview"
<AbsoluteLayout android:orientation="vertical"
android:id="@+id/topbox" android:layout_width="fill_parent"
android:layout_width="275px" android:layout_height="fill_parent">
android:layout_height="120px" <TableLayout android:id="@+id/mainLayout"
android:layout_x="19px" android:layout_gravity="center"
android:layout_y="23px" android:layout_height="wrap_content"
> android:layout_width="wrap_content">
<ImageView <TableRow android:id="@+id/startRow">
<TableLayout
android:layout_gravity="center"
android:layout_height="wrap_content"
android:layout_width="wrap_content">
<TableRow>
<ImageView
android:id="@+id/imgStatus" android:id="@+id/imgStatus"
android:layout_x="40px"
android:layout_y="11px"
android:layout_width="64px"
android:layout_height="64px"
>
</ImageView>
<TextView
android:id="@+id/lblStatus"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="TextView" android:src="@drawable/toroff"/>
android:layout_x="111px" </TableRow>
android:layout_y="39px" <TableRow>
android:textColor="#333333" <TextView android:id="@+id/lblStatus"
> android:text=" \n "
</TextView> android:paddingTop="15px"
</AbsoluteLayout> android:layout_gravity="center_horizontal"
<Button android:textStyle="bold"
android:id="@+id/btnStart" android:textColor="#ffffff" />
android:layout_width="205px" </TableRow>
android:layout_height="wrap_content" </TableLayout>
android:text="Start Tor" </TableRow>
android:layout_x="54px"
android:layout_y="133px" </TableLayout>
> </ScrollView>
</Button>
<Button
android:id="@+id/btnLog"
android:layout_width="206px" </LinearLayout>
android:layout_height="wrap_content"
android:text="View Message Log"
android:layout_x="54px"
android:layout_y="221px"
>
</Button>
<Button
android:id="@+id/btnSettings"
android:layout_width="206px"
android:layout_height="wrap_content"
android:text="View Settings"
android:layout_x="53px"
android:layout_y="305px"
>
</Button>
</AbsoluteLayout>

View File

@ -8,21 +8,21 @@
<ScrollView android:layout_height="380px" <ScrollView android:layout_height="380px"
android:layout_width="fill_parent"> android:layout_width="fill_parent">
<TextView android:id="@+id/textSettings" <EditText android:id="@+id/textSettings"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_weight="1.0" android:layout_weight="1.0"
android:singleLine="false"
/> />
</ScrollView> </ScrollView>
<Button android:id="@+id/btnSettingsClose" <Button android:id="@+id/btnSettingsSave"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="40px" android:layout_height="40px"
android:text="Close Log" android:text="Save Settings"
android:layout_margin="0sp" android:layout_margin="0sp"
/> />
</LinearLayout> </LinearLayout>

View File

@ -51,14 +51,21 @@ package org.torproject.android;
* *
* Julian Robichaux -- http://www.nsftools.com * Julian Robichaux -- http://www.nsftools.com
*/ */
import java.io.*; import java.io.BufferedInputStream;
import java.net.*; import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import net.sourceforge.jsocks.socks.Socks4Proxy;
import net.sourceforge.jsocks.socks.Socks5Proxy; import net.sourceforge.jsocks.socks.Socks5Proxy;
import net.sourceforge.jsocks.socks.SocksSocket; import net.sourceforge.jsocks.socks.SocksSocket;
public class HttpProxy extends Thread public class HttpProxy extends Thread
{ {
public static final int DEFAULT_PORT = 8888; public static final int DEFAULT_PORT = 8888;

View File

@ -54,11 +54,13 @@ public class TorBinaryInstaller implements TorConstants {
streamToFile(zip.getInputStream(zipen),TORRC_INSTALL_PATH); streamToFile(zip.getInputStream(zipen),TORRC_INSTALL_PATH);
zip.close(); zip.close();
Log.i(LOG_TAG,"SUCCESS: unzipped tor binary from apk");
} }
catch (IOException ioe) catch (IOException ioe)
{ {
Log.i(LOG_TAG,"unable to unzip tor binary from apk",ioe); Log.i(LOG_TAG,"FAIL: unable to unzip tor binary from apk",ioe);
} }
} }

View File

@ -28,6 +28,9 @@ public interface TorConstants {
//where to send the notices log //where to send the notices log
public final static String TOR_LOG_PATH = TOR_HOME + "notices.log"; public final static String TOR_LOG_PATH = TOR_HOME + "notices.log";
//control port cookie path
public final static String TOR_CONTROL_AUTH_COOKIE = TOR_HOME + "data/control_auth_cookie";
//how to launch tor //how to launch tor
public final static String TOR_COMMAND_LINE_ARGS = "-f " + TORRC_INSTALL_PATH; public final static String TOR_COMMAND_LINE_ARGS = "-f " + TORRC_INSTALL_PATH;
@ -55,5 +58,16 @@ public interface TorConstants {
//what is says! //what is says!
public final static String IP_LOCALHOST = "127.0.0.1"; public final static String IP_LOCALHOST = "127.0.0.1";
public final static int TOR_CONTROL_PORT = 9051;
public final static int UPDATE_TIMEOUT = 3000;
//status to communicate state
public final static int STATUS_OFF = 0;
public final static int STATUS_ON = 1;
public final static int STATUS_STARTING_UP = 2;
public final static int STATUS_SHUTTING_DOWN = 3;
//control port
public final static String TOR_CONTROL_PORT_MSG_BOOTSTRAP_DONE = "Bootstrapped 100%";
} }

View File

@ -6,18 +6,23 @@ package org.torproject.android;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
public class TorControlPanel extends Activity implements OnClickListener, TorConstants public class TorControlPanel extends Activity implements OnClickListener, TorConstants
{ {
@ -27,13 +32,15 @@ public class TorControlPanel extends Activity implements OnClickListener, TorCon
private Intent torService = null; private Intent torService = null;
private boolean updateLog = false; private boolean updateLog = false;
private boolean updateStatus = false;
/** Called when the activity is first created. */ /** Called when the activity is first created. */
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setTheme(android.R.style.Theme_Black);
showMain(); showMain();
@ -41,7 +48,50 @@ public class TorControlPanel extends Activity implements OnClickListener, TorCon
} }
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuItem mItem = menu.add(0, 1, Menu.NONE, "Home");
MenuItem mItem2 = menu.add(0, 2, Menu.NONE, "Settings");
MenuItem mItem3 = menu.add(0, 3, Menu.NONE, "Log");
MenuItem mItem4 = menu.add(0, 4, Menu.NONE, "Browser");
mItem.setIcon(R.drawable.ic_menu_home);
mItem2.setIcon(R.drawable.ic_menu_register);
mItem3.setIcon(R.drawable.ic_menu_reports);
mItem4.setIcon(R.drawable.ic_menu_goto);
return true;
}
/* (non-Javadoc)
* @see android.app.Activity#onMenuItemSelected(int, android.view.MenuItem)
*/
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
super.onMenuItemSelected(featureId, item);
if (item.getItemId() == 1)
{
this.showMain();
}
else if (item.getItemId() == 2)
{
this.showSettings();
}
else if (item.getItemId() == 3)
{
this.showMessageLog();
}
else if (item.getItemId() == 4)
{
Toast.makeText(this, "Not yet implemented!", Toast.LENGTH_SHORT);
}
return true;
}
/* (non-Javadoc) /* (non-Javadoc)
* @see android.app.Activity#onPause() * @see android.app.Activity#onPause()
@ -63,7 +113,7 @@ public class TorControlPanel extends Activity implements OnClickListener, TorCon
// TODO Auto-generated method stub // TODO Auto-generated method stub
super.onResume(); super.onResume();
setUIState (); checkStatus ();
} }
@ -77,7 +127,7 @@ public class TorControlPanel extends Activity implements OnClickListener, TorCon
// TODO Auto-generated method stub // TODO Auto-generated method stub
super.onStart(); super.onStart();
setUIState (); checkStatus ();
} }
@ -100,17 +150,32 @@ public class TorControlPanel extends Activity implements OnClickListener, TorCon
private void showMain () private void showMain ()
{ {
updateLog = false; updateLog = false;
updateStatus = true;
setContentView(R.layout.layout_main); setContentView(R.layout.layout_main);
((Button)findViewById(R.id.btnStart)).setOnClickListener(this); findViewById(R.id.imgStatus).setOnClickListener(this);
((Button)findViewById(R.id.btnLog)).setOnClickListener(this);
Thread thread = new Thread ()
((Button)findViewById(R.id.btnSettings)).setOnClickListener(this); {
public void run ()
{
setUIState ();
while (updateStatus)
{
handlerStatus.sendEmptyMessage(0);
try {
Thread.sleep(UPDATE_TIMEOUT);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
thread.start();
} }
/* /*
@ -119,8 +184,9 @@ public class TorControlPanel extends Activity implements OnClickListener, TorCon
private void showMessageLog () private void showMessageLog ()
{ {
setContentView(R.layout.layout_log); setContentView(R.layout.layout_log);
((Button)findViewById(R.id.btnLogClose)).setOnClickListener(this); ((Button)findViewById(R.id.btnLogClear)).setOnClickListener(this);
updateStatus = false;
updateLog = true; updateLog = true;
Thread thread = new Thread () Thread thread = new Thread ()
@ -130,13 +196,15 @@ public class TorControlPanel extends Activity implements OnClickListener, TorCon
while (updateLog) while (updateLog)
{ {
handler.sendEmptyMessage(0);
try { try {
Thread.sleep(5000); Thread.sleep(UPDATE_TIMEOUT);
} catch (InterruptedException e) { } catch (InterruptedException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
} }
handler.sendEmptyMessage(0);
} }
} }
}; };
@ -153,9 +221,12 @@ public class TorControlPanel extends Activity implements OnClickListener, TorCon
TextView tvLog = (TextView)findViewById(R.id.messageLog); TextView tvLog = (TextView)findViewById(R.id.messageLog);
String output = loadLogFile(TOR_LOG_PATH); if (tvLog != null)
{
String output = loadTextFile(TOR_LOG_PATH);
tvLog.setText(output); tvLog.setText(output);
}
} }
@ -172,6 +243,21 @@ public class TorControlPanel extends Activity implements OnClickListener, TorCon
} }
}; };
/*
* Handle to reload Tor debug log every few seconds while viewing it
*/
private Handler handlerStatus = new Handler() {
@Override
public void handleMessage(Message msg) {
checkStatus();
}
};
/* /*
* Load the basic settings application to display torrc * Load the basic settings application to display torrc
@ -179,14 +265,17 @@ public class TorControlPanel extends Activity implements OnClickListener, TorCon
*/ */
private void showSettings () private void showSettings ()
{ {
updateStatus = false;
updateLog = false;
setContentView(R.layout.layout_settings); setContentView(R.layout.layout_settings);
TextView tvLog = (TextView)findViewById(R.id.textSettings);
((Button)findViewById(R.id.btnSettingsClose)).setOnClickListener(this);
String output = loadLogFile(TORRC_INSTALL_PATH);
tvLog.setText(output); String output = loadTextFile(TORRC_INSTALL_PATH);
TextView tvSettings = (TextView)findViewById(R.id.textSettings);
((Button)findViewById(R.id.btnSettingsSave)).setOnClickListener(this);
tvSettings.setText(output);
} }
@ -194,28 +283,42 @@ public class TorControlPanel extends Activity implements OnClickListener, TorCon
/* /*
* Set the state of the running/not running graphic and label * Set the state of the running/not running graphic and label
*/ */
public void setUIState () public void checkStatus ()
{ {
TextView lblStatus = (TextView)findViewById(R.id.lblStatus); TextView lblStatus = (TextView)findViewById(R.id.lblStatus);
ImageView imgStatus = (ImageView)findViewById(R.id.imgStatus); ImageView imgStatus = (ImageView)findViewById(R.id.imgStatus);
Button btnStart = (Button)findViewById(R.id.btnStart); if (imgStatus != null)
if (TorService.isRunning())
{ {
btnStart.setText("Stop Tor"); int torStatus = TorService.getStatus();
imgStatus.setImageResource(R.drawable.toron);
lblStatus.setText("Tor is running"); if (torStatus == STATUS_ON)
{
} imgStatus.setImageResource(R.drawable.toron);
else lblStatus.setText("Tor is running\n- touch to stop -");
{ updateStatus = false;
btnStart.setText("Start Tor"); }
imgStatus.setImageResource(R.drawable.toroff); else if (torStatus == STATUS_STARTING_UP)
lblStatus.setText("Tor is not running"); {
imgStatus.setImageResource(R.drawable.torstarting);
lblStatus.setText("Tor is starting up\n(this might take a little bit)");
}
else if (torStatus == STATUS_SHUTTING_DOWN)
{
imgStatus.setImageResource(R.drawable.torstopping);
lblStatus.setText("Tor is shutting down\nplease wait...");
}
else
{
imgStatus.setImageResource(R.drawable.toroff);
lblStatus.setText("Tor is not running\n- touch to start -");
updateStatus = false;
}
} }
@ -229,59 +332,54 @@ public class TorControlPanel extends Activity implements OnClickListener, TorCon
public void onClick(View view) { public void onClick(View view) {
// the start button // the start button
if (view.getId()==R.id.btnStart) if (view.getId()==R.id.imgStatus)
{ {
//if Tor binary is not running, then start the service up //if Tor binary is not running, then start the service up
if (!TorService.isRunning()) if (TorService.getStatus()==STATUS_OFF)
{ {
torService = new Intent(this, TorService.class); torService = new Intent(this, TorService.class);
torService.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); torService.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
TorService.setActivity(this); TorService.setActivity(this);
startService(torService); startService(torService);
} }
else else if (TorService.getStatus()==STATUS_ON)
{ {
if (torService == null)
torService = new Intent(this, TorService.class);
//stopService(torService);
TorService.setActivity(this); TorService.stopTor ();
stopService(torService);
} }
//update the UI
setUIState ();
}
else if (view.getId()==R.id.btnLog)
{
showMessageLog();
showMain ();
} }
else if (view.getId()==R.id.btnSettings) else if (view.getId()==R.id.btnLogClear)
{
showSettings();
}
else if (view.getId()==R.id.btnLogClose || view.getId()==R.id.btnSettingsClose)
{ {
showMain(); saveTextFile(TOR_LOG_PATH,"");
}
else if (view.getId()==R.id.btnSettingsSave)
{
TextView tvSettings = (TextView)findViewById(R.id.textSettings);
String newSettings = tvSettings.getText().toString();
saveTextFile(TORRC_INSTALL_PATH, newSettings);
} }
} }
/* /*
* Load the log file text * Load the log file text
*/ */
public static String loadLogFile (String path) public static String loadTextFile (String path)
{ {
String line = null; String line = null;
@ -305,6 +403,34 @@ public class TorControlPanel extends Activity implements OnClickListener, TorCon
} }
/*
* Load the log file text
*/
public static boolean saveTextFile (String path, String contents)
{
try {
FileWriter writer = new FileWriter( path, false );
writer.write( contents );
writer.close();
return true;
} catch (IOException e) {
// Log.i(TAG, "error writing file: " + path, e);
e.printStackTrace();
return false;
}
}
/* /*
* Get the last line of the log file for status display * Get the last line of the log file for status display

View File

@ -5,11 +5,20 @@ package org.torproject.android;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.Socket;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import net.freehaven.tor.control.EventHandler;
import net.freehaven.tor.control.NullEventHandler;
import net.freehaven.tor.control.TorControlConnection;
import net.sourceforge.jsocks.socks.Proxy; import net.sourceforge.jsocks.socks.Proxy;
import android.app.Service; import android.app.Service;
import android.content.Intent; import android.content.Intent;
@ -28,22 +37,29 @@ public class TorService extends Service implements TorConstants
private static Process procTor = null; private static Process procTor = null;
private static int currentStatus = STATUS_OFF;
private static TorControlConnection conn = null;
/** Called when the activity is first created. */ /** Called when the activity is first created. */
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
} }
public static boolean isRunning ()
public static int getStatus ()
{ {
int procId = findProcessId(TorConstants.TOR_BINARY_INSTALL_PATH); try {
getTorStatus();
return (procId != -1); } catch (IOException e) {
Log.i(TAG,"Unable to get tor status",e);
}
return currentStatus;
} }
/* (non-Javadoc) /* (non-Javadoc)
@ -74,13 +90,18 @@ public class TorService extends Service implements TorConstants
private void startService () private void startService ()
{ {
Log.i(TAG,"Tor thread started"); Thread thread = new Thread ()
{
public void run ()
initTor(); {
Log.i(TAG,"Tor thread started");
setupWebProxy(true);
initTor();
}
};
thread.start();
} }
@ -90,9 +111,25 @@ public class TorService extends Service implements TorConstants
public void onDestroy () public void onDestroy ()
{ {
killTorProcess (); }
setupWebProxy(false);
public static void stopTor ()
{
currentStatus = STATUS_SHUTTING_DOWN;
Thread thread = new Thread ()
{
public void run ()
{
killTorProcess ();
setupWebProxy(false);
currentStatus = STATUS_OFF;
}
};
thread.start();
} }
@ -100,7 +137,7 @@ public class TorService extends Service implements TorConstants
ACTIVITY = activity; ACTIVITY = activity;
} }
private void setupWebProxy (boolean enabled) private static void setupWebProxy (boolean enabled)
{ {
if (enabled) if (enabled)
{ {
@ -140,91 +177,142 @@ public class TorService extends Service implements TorConstants
if (webProxy != null) if (webProxy != null)
{ {
showToast("Tor is disabled - browsing is not anonymous!"); //logNotice("Tor is disabled - browsing is not anonymous!");
webProxy.setDoSocks(false); //webProxy.setDoSocks(false);
} }
} }
} }
private void killTorProcess () public static void reloadConfig ()
{
try
{
if (conn == null)
{
initControlConnection ();
}
if (conn != null)
{
conn.signal("RELOAD");
}
}
catch (Exception e)
{
Log.i(TAG,"Unable to reload configuration",e);
}
}
private void shutdownTor ()
{
try
{
currentStatus = STATUS_SHUTTING_DOWN;
if (conn == null)
{
initControlConnection ();
}
if (conn != null)
{
conn.signal("SHUTDOWN");
}
}
catch (Exception e)
{
}
}
private static void killTorProcess ()
{ {
//doCommand(SHELL_CMD_KILLALL, CHMOD_EXE_VALUE, TOR_BINARY_INSTALL_PATH); //doCommand(SHELL_CMD_KILLALL, CHMOD_EXE_VALUE, TOR_BINARY_INSTALL_PATH);
/*
if (procTor != null) if (procTor != null)
{ {
Log.i(TAG,"shutting down Tor process..."); Log.i(TAG,"shutting down Tor process...");
procTor.destroy(); procTor.destroy();
try { try {
procTor.waitFor(); procTor.waitFor();
} }
catch(Exception e) catch(Exception e2)
{ {
e.printStackTrace(); e2.printStackTrace();
} }
int exitStatus = procTor.exitValue(); int exitStatus = procTor.exitValue();
Log.i(TAG,"Tor exit: " + exitStatus); Log.i(TAG,"Tor exit: " + exitStatus);
procTor = null; procTor = null;
} }*/
int procId = findProcessId(TorConstants.TOR_BINARY_INSTALL_PATH); int procId = findProcessId(TorConstants.TOR_BINARY_INSTALL_PATH);
if (procId != -1) if (procId != -1)
{ {
Log.i(TAG,"Found Tor PID=" + procId + " - killing now..."); Log.i(TAG,"Found Tor PID=" + procId + " - killing now...");
doCommand(SHELL_CMD_KILLALL, procId + ""); doCommand(SHELL_CMD_KILLALL, procId + "");
} }
if (ACTIVITY != null) conn = null;
((TorControlPanel)ACTIVITY).setUIState();
} }
private void showToast (String msg) private static void logNotice (String msg)
{ {
Toast toast = Toast.makeText(ACTIVITY, msg, Toast.LENGTH_LONG); Log.i(TAG, msg);
toast.show();
} }
private void checkBinary ()
{
boolean binaryExists = new File(TOR_BINARY_INSTALL_PATH).exists();
if (!binaryExists)
{
killTorProcess ();
TorBinaryInstaller installer = new TorBinaryInstaller();
installer.start(true);
binaryExists = new File(TOR_BINARY_INSTALL_PATH).exists();
if (binaryExists)
{
logNotice("Tor binary installed!");
}
else
{
logNotice("Tor binary install FAILED!");
return;
}
}
Log.i(TAG,"Setting permission on Tor binary");
doCommand(SHELL_CMD_CHMOD, CHMOD_EXE_VALUE + ' ' + TOR_BINARY_INSTALL_PATH);
}
public void initTor () public void initTor ()
{ {
try { try {
currentStatus = STATUS_STARTING_UP;
boolean binaryExists = new File(TOR_BINARY_INSTALL_PATH).exists();
if (!binaryExists)
{
TorBinaryInstaller installer = new TorBinaryInstaller();
installer.start(false);
binaryExists = new File(TOR_BINARY_INSTALL_PATH).exists();
if (binaryExists)
{
showToast("Tor binary installed!");
}
else
{
showToast("Tor binary install FAILED!");
return;
}
}
Log.i(TAG,"Setting permission on Tor binary");
doCommand(SHELL_CMD_CHMOD, CHMOD_EXE_VALUE + ' ' + TOR_BINARY_INSTALL_PATH);
killTorProcess (); killTorProcess ();
checkBinary ();
doCommand(SHELL_CMD_RM,TOR_LOG_PATH); doCommand(SHELL_CMD_RM,TOR_LOG_PATH);
Log.i(TAG,"Starting tor process"); Log.i(TAG,"Starting tor process");
@ -232,11 +320,13 @@ public class TorService extends Service implements TorConstants
//Log.i(TAG,"Tor process id=" + procTor.); //Log.i(TAG,"Tor process id=" + procTor.);
showToast("Tor is starting up..."); currentStatus = STATUS_STARTING_UP;
logNotice("Tor is starting up...");
((TorControlPanel)ACTIVITY).setUIState(); Thread.sleep(2000);
initControlConnection ();
} catch (Exception e) {
} catch (Exception e) {
Log.w(TAG,"unable to start Tor Process",e); Log.w(TAG,"unable to start Tor Process",e);
@ -311,7 +401,7 @@ public class TorService extends Service implements TorConstants
} }
public Process doCommand(String command, String arg1) public static Process doCommand(String command, String arg1)
{ {
Runtime r = Runtime.getRuntime(); Runtime r = Runtime.getRuntime();
@ -335,57 +425,161 @@ public class TorService extends Service implements TorConstants
return child; return child;
} }
/*
public static String doCommand(String command, String arg0, String arg1, boolean logOutput) {
try {
// android.os.Exec is not included in android.jar so we need to use reflection.
Class execClass = Class.forName("android.os.Exec");
Method createSubprocess = execClass.getMethod("createSubprocess",
String.class, String.class, String.class, int[].class);
Method waitFor = execClass.getMethod("waitFor", int.class);
// Executes the command.
// NOTE: createSubprocess() is asynchronous.
int[] pid = new int[1];
FileDescriptor fd = (FileDescriptor)createSubprocess.invoke(
null, command, arg0, arg1, pid);
StringBuffer output = new StringBuffer();
if (logOutput)
{
// Reads stdout.
// NOTE: You can write to stdin of the command using new FileOutputStream(fd).
FileInputStream in = new FileInputStream(fd);
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
try {
String line;
while ((line = reader.readLine()) != null) {
output.append(line);
output.append('\n');
}
} catch (IOException e) {
// It seems IOException is thrown when it reaches EOF.
Log.e(TAG, "error reading output file", e);
} public static String generateHashPassword ()
{
/*
PasswordDigest d = PasswordDigest.generateDigest();
byte[] s = d.getSecret(); // pass this to authenticate
String h = d.getHashedPassword(); // pass this to the Tor on startup.
*/
return null;
}
public static synchronized void initControlConnection () throws Exception, RuntimeException
{
if (conn == null)
{
Log.i(TAG,"Connecting to control port: " + TOR_CONTROL_PORT);
Socket s = new Socket(IP_LOCALHOST, TOR_CONTROL_PORT);
conn = TorControlConnection.getConnection(s);
// conn.authenticate(new byte[0]); // See section 3.2
Log.i(TAG,"SUCCESS connected to control port");
//
File fileCookie = new File(TOR_CONTROL_AUTH_COOKIE);
byte[] cookie = new byte[(int)fileCookie.length()];
new FileInputStream(new File(TOR_CONTROL_AUTH_COOKIE)).read(cookie);
conn.authenticate(cookie);
Log.i(TAG,"SUCCESS authenticated to control port");
addEventHandler();
}
}
public void modifyConf () throws IOException
{
// Get one configuration variable.
List options = conn.getConf("contact");
// Get a set of configuration variables.
// List options = conn.getConf(Arrays.asList(new String[]{
// "contact", "orport", "socksport"}));
// Change a single configuration variable
conn.setConf("BandwidthRate", "1 MB");
// Change several configuration variables
conn.setConf(Arrays.asList(new String[]{
"HiddenServiceDir /home/tor/service1",
"HiddenServicePort 80",
}));
// Reset some variables to their defaults
conn.resetConf(Arrays.asList(new String[]{
"contact", "socksport"
}));
// Flush the configuration to disk.
conn.saveConf();
}
private static void getTorStatus () throws IOException
{
try
{
if (conn == null && (currentStatus == STATUS_STARTING_UP || currentStatus == STATUS_ON))
{
initControlConnection ();
// Waits for the command to finish.
waitFor.invoke(null, pid[0]);
} }
if (conn != null)
{
// get a single value.
// get several values
if (currentStatus == STATUS_STARTING_UP)
{
//Map vals = conn.getInfo(Arrays.asList(new String[]{
// "status/bootstrap-phase", "status","version"}));
String bsPhase = conn.getInfo("status/bootstrap-phase");
// send output to the textbox // Log.i(TAG, "bootstrap-phase: " + bsPhase);
return output.toString();
if (bsPhase.indexOf("PROGRESS=100")!=-1)
} {
currentStatus = STATUS_ON;
}
}
else
{
// String status = conn.getInfo("status/circuit-established");
// Log.i(TAG, "status/circuit-established=" + status);
}
}
}
catch (Exception e) catch (Exception e)
{ {
Log.i(TAG, "unable to execute command",e); Log.i(TAG, "Unable to get Tor status from control port");
e.printStackTrace();
return null;
} }
}*/
}
/*
* The recognized signal names are:
"RELOAD" -- Reload configuration information
"SHUTDOWN" -- Start a clean shutdown of the Tor process
"DUMP" -- Write current statistics to the logs
"DEBUG" -- Switch the logs to debugging verbosity
"HALT" -- Stop the Tor process immediately.
*/
public void sendSignal () throws IOException
{
conn.signal("RELOAD");
}
public static void addEventHandler () throws IOException
{
// We extend NullEventHandler so that we don't need to provide empty
// implementations for all the events we don't care about.
// ...
Log.i(TAG,"adding control port event handler");
EventHandler eh = new NullEventHandler()
{
public void message(String severity, String msg) {
// Log.println(priority, tag, msg)("["+severity+"] "+msg);
//Toast.makeText(, text, duration)
// Toast.makeText(ACTIVITY, severity + ": " + msg, Toast.LENGTH_SHORT);
Log.i(TAG, "[Tor Control Port] " + severity + ": " + msg);
if (msg.indexOf(TOR_CONTROL_PORT_MSG_BOOTSTRAP_DONE)!=-1)
{
currentStatus = STATUS_ON;
setupWebProxy(true);
}
}
};
conn.setEventHandler(eh);
conn.setEvents(Arrays.asList(new String[]{
"ORCONN", "CIRC", "INFO", "NOTICE", "ERR"}));
// conn.setEvents(Arrays.asList(new String[]{
// "DEBUG", "INFO", "NOTICE", "WARN", "ERR"}));
Log.i(TAG,"SUCCESS added control port event handler");
}
} }