package info.guardianproject.iocipher.server;
/*
* Includes code from:
* http://code.google.com/p/swiftp/source/browse/trunk/src/org/swiftp/FTPServerService.java#482
Copyright 2009 David Revell
This file is part of SwiFTP.
SwiFTP is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
SwiFTP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with SwiFTP. If not, see <http://www.gnu.org/licenses/>.
*/
import info.guardianproject.iocipher.server.WebServerService.LocalBinder;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.StringTokenizer;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.actionbarsherlock.app.SherlockActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
public class IOCipherServerActivity extends SherlockActivity {
public final static String TAG = "IOCipherServer";
private final String ksFileName = "iocipher.bks";
private final String ksAlias = "twjs";
private final static String LOCALHOST = "127.0.0.1";
boolean mBound = false;
private WebServerService mService;
private final static int DEFAULT_PORT = 8443;
private int mWsPort = -1;
private boolean mWsUseSSL = true;
private boolean runOnBind = false;
private String adminPwd = null;
private MenuItem mMenuStartTop;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
private void askForPassword ()
{
// This example shows how to add a custom layout to an AlertDialog
LayoutInflater factory = LayoutInflater.from(this);
final View textEntryView = factory.inflate(R.layout.alert_dialog_text_entry, null);
new AlertDialog.Builder(this)
.setTitle(getString(R.string.app_name))
.setView(textEntryView)
.setMessage(R.string.password_msg)
.setPositiveButton(getString(R.string.button_ok), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
EditText eText = ((android.widget.EditText)textEntryView.findViewById(R.id.password_edit));
adminPwd = eText.getText().toString();
postBound();
}
})
.setNegativeButton(getString(R.string.button_cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
/* User clicked cancel so do some stuff */
IOCipherServerActivity.this.finish();
}
})
.create().show();
}
@Override
protected void onResume() {
super.onResume();
bindService();
checkForImports();
}
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
public void bindService ()
{
Intent intent = new Intent(this, WebServerService.class);
startService(intent);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
public void startWebServer()
{
if (mService == null)
{
runOnBind = true;
bindService();
}
else if (!mService.isServerRunning())
{
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
mWsUseSSL = prefs.getBoolean("useSSL", true);
mWsPort = Integer.parseInt(prefs.getString("prefPort", "" + DEFAULT_PORT));
try
{
mService.startServer(mWsPort, mWsUseSSL, getNetworkAddress(), adminPwd);
showStatus();
}
catch (IllegalArgumentException e)
{
adminPwd = null;
Log.e(TAG, "unable to start secure server",e);
}
catch (Exception e)
{
Log.e(TAG, "unable to start secure server",e);
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
}
public void stopWebServer ()
{
mService.stopServer();
clearStatus ();
}
private void postBound ()
{
if (!mService.isServerRunning())
{
if (adminPwd == null)
{
askForPassword ();
}
else if (runOnBind)
{
startWebServer();
}
}
else
{
showStatus();
new Thread ()
{
public void run ()
{
handleImport();
}
}.start();
}
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
postBound ();
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
private void showStatus ()
{
TextView tv = (TextView)findViewById(R.id.textStatus);
StringBuffer msg = new StringBuffer();
String ip = getNetworkAddress();
msg.append("Wifi IP: ").append(ip);
msg.append("\n\n");
if (mService != null && mService.isServerRunning())
{
if (mMenuStartTop != null)
mMenuStartTop.setIcon(android.R.drawable.ic_media_pause);
String protocol = "https";
if (!mWsUseSSL)
protocol = "http";
msg.append("Public Share:").append('\n');
msg.append(protocol).append("://").append(ip).append(':').append(mWsPort).append("/public/");
msg.append("\n\n");
msg.append("Private Share:").append('\n');
msg.append(protocol).append("://").append(ip).append(':').append(mWsPort).append("/private/");
msg.append("\n\n");
if (adminPwd != null)
{
String fingerprint = "";
File fileKS = new File(this.getFilesDir(),ksFileName);
if (fileKS.exists())
{
CACertManager ccm = new CACertManager();
try {
ccm.load(fileKS.getAbsolutePath(), adminPwd);
fingerprint = ccm.getFingerprint(ccm.getCertificateChain(ksAlias)[0], "SHA1");
msg.append("SHA1 Fingerprint").append('\n');
msg.append(fingerprint);
} catch (Exception e) {
Log.e(TAG,"error loading fingerprint",e);
}
}
}
}
else
{
if (mMenuStartTop != null)
mMenuStartTop.setIcon(android.R.drawable.ic_media_play);
msg.append("(Server deactivated)");
}
tv.setText(msg.toString());
}
private void clearStatus ()
{
TextView tv = (TextView)findViewById(R.id.textStatus);
tv.setText("");
}
@Override
protected void onStart() {
super.onStart();
}
private String getNetworkAddress() {
ConnectivityManager connMgr = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
final android.net.NetworkInfo wifi = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
final android.net.NetworkInfo mobile = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
if (wifi.isAvailable()) {
WifiManager myWifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
WifiInfo myWifiInfo = myWifiManager.getConnectionInfo();
int ip = myWifiInfo.getIpAddress();
String ipString = android.text.format.Formatter.formatIpAddress(ip);
Log.w(TAG, "Wifi address: " + ipString);
return ipString;
} else if (mobile.isAvailable()) {
Log.w(TAG, "No wifi available (mobile, yes)");
} else {
Log.w(TAG, "No network available");
}
return null;
}
public static byte byteOfInt(int value, int which) {
int shift = which * 8;
return (byte)(value >> shift);
}
public static InetAddress intToInet(int value) {
byte[] bytes = new byte[4];
for(int i = 0; i<4; i++) {
bytes[i] = byteOfInt(value, i);
}
try {
return InetAddress.getByAddress(bytes);
} catch (UnknownHostException e) {
// This only happens if the byte array has a bad length
return null;
}
}
public static final int byteArrayToInt(byte[] arr, int offset) {
if (arr == null || arr.length - offset < 4)
return -1;
int r0 = (arr[offset] & 0xFF) << 24;
int r1 = (arr[offset + 1] & 0xFF) << 16;
int r2 = (arr[offset + 2] & 0xFF) << 8;
int r3 = arr[offset + 3] & 0xFF;
return r0 + r1 + r2 + r3;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
mMenuStartTop = menu.add(Menu.NONE,1,Menu.NONE,"Start");
mMenuStartTop.setIcon(android.R.drawable.ic_media_play)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
menu.add(Menu.NONE,2,Menu.NONE,"Settings")
.setIcon(android.R.drawable.ic_menu_preferences)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add(Menu.NONE,3,Menu.NONE,"About")
.setIcon(android.R.drawable.ic_menu_info_details)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId())
{
case (1):
if (!mService.isServerRunning())
{
startWebServer();
item.setIcon(android.R.drawable.ic_media_pause);
}
else
{
stopWebServer();
item.setIcon(android.R.drawable.ic_media_play);
}
break;
case (2):
showPrefs();
break;
case (3):
showAbout ();
break;
default:
}
return super.onOptionsItemSelected(item);
}
private void showPrefs ()
{
Intent intent = new Intent (this, IOCipherSettingsActivity.class);
startActivity(intent);
}
private void showAbout ()
{
new AlertDialog.Builder(this)
.setTitle(getString(R.string.app_name))
.setMessage(getString(R.string.about))
.create().show();
}
Uri intentData = null;
private void checkForImports ()
{
intentData = getIntent().getData();
if (intentData == null && getIntent().hasExtra(Intent.EXTRA_STREAM))
intentData = (Uri) getIntent().getExtras().get(Intent.EXTRA_STREAM);
}
private void handleImport ()
{
if (intentData != null)
{
try
{
if (mService != null)
{
Message msg = new Message();
msg.getData().putInt("action", 0);
handler.sendMessage(msg);
mService.importFileToSecureStore(intentData);
Message msg2 = new Message();
msg2.getData().putInt("action", 1);
handler.sendMessage(msg2);
}
}
catch (Exception ioe)
{
Log.e(TAG,"error importing",ioe);
}
intentData = null;
}
}
ProgressDialog pd;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
int action = msg.getData().getInt("action");
if (action==0)
{
pd = ProgressDialog.show(IOCipherServerActivity.this, "Working..", "Importing File To Secure Store", true,
false);
}
else if (action == 1)
{
if (pd != null)
pd.cancel();
}
else if (action == 2)
{
String status = msg.getData().getString("status");
if (pd != null)
pd.setMessage(status);
}
}
};
}