package info.guardianproject.iocipher.server; import info.guardianproject.iocipher.File; import info.guardianproject.iocipher.FileOutputStream; import info.guardianproject.iocipher.FileWriter; import info.guardianproject.iocipher.VirtualFileSystem; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import android.app.Notification; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Binder; import android.os.IBinder; import android.provider.MediaStore.Images; import android.util.Log; public class WebServerService extends Service { private final static String TAG = "IOCipherServer"; private MdnsManager mdns; // Binder given to clients private final IBinder mBinder = new LocalBinder(); private Thread mWsThread; private int mPort; private boolean mUseSsl; private String mIpAddress; private String IOCIPHER_FOLDER = "iocipher"; private String IOCIPHER_FILE = "iocipher.db"; private VirtualFileSystem vfs; /** * Class used for the client Binder. Because we know this service always * runs in the same process as its clients, we don't need to deal with IPC. */ public class LocalBinder extends Binder { WebServerService getService() { // Return this instance of LocalService so clients can call public methods return WebServerService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } class MyServ extends Acme.Serve.Serve { // Overriding method for public access public void setMappingTable(PathTreeDictionary mappingtable) { super.setMappingTable(mappingtable); } // add the method below when .war deployment is needed public void addWarDeployer(String deployerFactory, String throttles) { super.addWarDeployer(deployerFactory, throttles); } }; MyServ srv; public MyServ getWebServer () { return srv; } private void startNotification () { //This constructor is deprecated. Use Notification.Builder instead Notification notice = new Notification(R.drawable.iocipher, "Active: " + mIpAddress, System.currentTimeMillis()); Intent intent = new Intent(WebServerService.this, IOCipherServerActivity.class); PendingIntent pendIntent = PendingIntent.getActivity(WebServerService.this, 0, intent, 0); //This method is deprecated. Use Notification.Builder instead. notice.setLatestEventInfo(WebServerService.this, "IOCipherServer", "Active: " + mIpAddress, pendIntent); notice.flags |= Notification.FLAG_NO_CLEAR; startForeground(mPort,notice); } public void startServer (int port, boolean useSSL, String ipAddress, final String password) throws IllegalArgumentException, Exception { // android.os.Debug.waitForDebugger(); mPort = port; mUseSsl = useSSL; mIpAddress = ipAddress; srv = new MyServ(); java.io.File filePublic = new java.io.File("/sdcard/public"); if (!filePublic.exists()) { filePublic.mkdir(); } // setting aliases, for an optional file servlet //Acme.Serve.Serve.PathTreeDictionary aliases = new Acme.Serve.Serve.PathTreeDictionary(); //aliases.put("/public/*", filePublic); // note cast name will depend on the class name, since it is anonymous class //srv.setMappingTable(aliases); // setting properties for the server, and exchangeable Acceptors java.util.Properties properties = new java.util.Properties(); properties.put("port", mPort); properties.setProperty(Acme.Serve.Serve.ARG_NOHUP, "nohup"); if (mUseSsl) { properties.setProperty("secure", "true"); //properties.setProperty("socketFactory", "Acme.Serve.SSLServerSocketFactory"); properties.setProperty("acceptorImpl", "Acme.Serve.SSLAcceptor"); java.io.File fileKS = new java.io.File(WebServerService.this.getFilesDir(),"iocipher.bks"); if (!fileKS.exists()) { String alias = "twjs"; String cn = "localhost"; String on = "iocipher"; KeyStoreGenerator.generateKeyStore(fileKS, alias, 1024, password, cn, on, on, "xx", "xx", "xx"); } properties.setProperty("keystoreFile",fileKS.getAbsolutePath()); properties.setProperty("keystorePass",password); properties.setProperty("keystoreType",CACertManager.KEYSTORE_TYPE); } srv.arguments = properties; srv.addServlet("/public/*", new FileServlet(filePublic)); java.io.File fileIoCipherDb = new java.io.File(getDir(IOCIPHER_FOLDER, Context.MODE_PRIVATE).getAbsoluteFile(),IOCIPHER_FILE); if (vfs == null) { setUpIOCipher(fileIoCipherDb, password); } srv.addServlet("/private/*", new IOCipherFileServlet(WebServerService.this)); // srv.addDefaultServlets(null); // optional file servlet /* String davUser = "admin"; DavServlet dServlet = new DavServlet(new java.io.File("/sdcard"),"sdcard", davUser, password); srv.addServlet("/mount/*", dServlet); */ startNotification(); mWsThread = new Thread () { public void run () { try { srv.serve(); } catch (Exception e) { handleException ("error starting server", e); } } }; mWsThread.start(); } public void startBroadcast (int port, boolean useSSL, String ipAddress) throws Exception { mPort = port; mUseSsl = useSSL; mIpAddress = ipAddress; mWsThread = new Thread () { public void run () { try { try { if (mdns == null) mdns = new MdnsManager(WebServerService.this); //mdns.register("iocs", "_webdavs._tcp.local", "iocipherwebdav", 8888, "path=/sdcard"); mdns.register("iocs-https", "_https._tcp.local", "iocipherweb", mPort, "path=/public"); } catch (Exception e) { Log.d(TAG, "mdns multicast not working"); } } catch (Exception e) { handleException ("error starting server", e); } } }; mWsThread.start(); } private void handleException (String msg, Exception e) { Log.e(TAG, msg, e); } public void stopServer () { if (mWsThread.isAlive()) { mWsThread.interrupt(); mWsThread = null; } if (srv != null) { srv.notifyStop(); srv = null; } if (mdns != null) { mdns.unregister("iocs"); mdns.unregister("iocs-https"); } stopForeground(true); if (vfs != null) vfs.unmount(); } public boolean isServerRunning () { if (mWsThread != null && mWsThread.isAlive()) return true; else return false; } @Override public void onDestroy() { super.onDestroy(); stopServer (); } /* @Override public int onStartCommand(Intent intent, int flags, int startId) { if (srv == null) { new Thread(this).start(); } return Service.START_STICKY; }*/ public void importFileToSecureStore (Uri uriSrc) throws IOException { String targetName = getName(uriSrc); info.guardianproject.iocipher.File fileNew = new info.guardianproject.iocipher.File(targetName); int i = 1; while (fileNew.exists()) { fileNew = new info.guardianproject.iocipher.File((i++) + '.' + targetName); } InputStream is = getContentResolver().openInputStream(uriSrc); copyStreamToFile (is, fileNew); } private String getName (Uri uri) { String name = null; if (uri != null) { Cursor c = getContentResolver().query(uri, null, null, null, null); if (c != null && c.moveToFirst()) { int id = c.getColumnIndex(Images.Media.DATA); if (id != -1) { name = c.getString(id); if (name != null) return new File(name).getName(); } id = c.getColumnIndex(Images.Media.DISPLAY_NAME); if (id != -1) { name = c.getString(id); } } } return name; } private static void copyStreamToFile(InputStream input, File fileOut) throws IOException { BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(fileOut)); byte[] buf = new byte[8096]; int len = -1; while ((len = input.read(buf))!=-1) { out.write(buf,0,len); } try { input.close(); } catch (IOException ignore) { Log.e(TAG, "error closing input",ignore); } try { out.close(); } catch (IOException ignore) { Log.e(TAG, "error closing output",ignore); } } protected synchronized void setUpIOCipher(java.io.File db, String password) throws IllegalArgumentException { Log.v("IOCipher", "database file: " + db.getAbsolutePath()); if (db.exists()) Log.v("IOCipher", "exists: " + db.getAbsolutePath()); try { vfs = new VirtualFileSystem(db.getAbsolutePath()); } catch (Exception e) { Log.e("IOCipher", e.toString()); } vfs.mount(password); } }