Add SH and ROOTSH commands
[fonbot.git] / src / ro / ieval / fonbot / Heavy.java
index c5c6de7462450b04f5529aaf38dc6621216e7991..b1f443c5f6067fb8a1366f2e51e71777d3504b32 100644 (file)
@@ -3,15 +3,22 @@ package ro.ieval.fonbot;
 import static ro.ieval.fonbot.R.string.*;
 import static ro.ieval.fonbot.Utils.toNonNull;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.net.UnknownHostException;
+import java.net.URL;
+import java.net.URLConnection;
 import java.nio.channels.FileChannel;
 import java.nio.channels.SocketChannel;
 import java.util.ArrayList;
@@ -53,6 +60,7 @@ import android.os.BatteryManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.PowerManager;
+import android.os.SystemClock;
 import android.os.Vibrator;
 import android.preference.PreferenceManager;
 import android.provider.BaseColumns;
@@ -168,6 +176,12 @@ final class Heavy {
                        sb.append(toNonNull(context.getString(at)));
                        sb.append(" ");
                        sb.append(locationDate.toString());
+                       sb.append(". ");
+
+                       sb.append("http://openstreetmap.org/?zoom=15&mlat=");
+                       sb.append(loc.getLatitude());
+                       sb.append("&mlon=");
+                       sb.append(loc.getLongitude());
                        Utils.sendMessage(toNonNull(context), toNonNull(replyTo), toNonNull(sb.toString()));
                }
 
@@ -281,6 +295,153 @@ final class Heavy {
                }
        }
 
+       /**
+        * ExecutableRunnable that uploads a file to a given host and port, netcat-style
+        *
+        * @author Marius Gavrilescu <marius@ieval.ro>
+        */
+       private static final class NcfileExecutableRunnable extends ExecutableRunnable {
+               private final Context context;
+               private final Address replyTo;
+               private final String filename;
+               private final String hostname;
+               private final int port;
+
+               /**
+                * Construct a NcfileExecutableRunnable
+                *
+                * @param context Context instance
+                * @param replyTo reply Address
+                * @param filename filename to upload
+                * @param hostname hostname to upload to
+                * @param port port to upload to
+                */
+               NcfileExecutableRunnable(final Context context, final Address replyTo, final String filename, final String hostname, final int port){
+                       this.context=context;
+                       this.replyTo=replyTo;
+                       this.filename=filename;
+                       this.hostname=hostname;
+                       this.port=port;
+               }
+
+               @Override public void run(){
+                       final FileChannel in;
+                       final SocketChannel sock;
+                       try{
+                               in=new FileInputStream(filename).getChannel();
+                       } catch (final FileNotFoundException e){
+                               Utils.sendMessage(context, replyTo, file_not_found, filename);
+                               return;
+                       }
+
+                       try{
+                               sock = SocketChannel.open(new InetSocketAddress(hostname, port));
+                       } catch (final IOException e){
+                               Utils.sendMessage(context, replyTo, toNonNull(context.getString(cannot_connect_to_host_on_port, hostname, Integer.valueOf(port))));
+                               try {
+                                       in.close();
+                               } catch (IOException ex) {
+                                       //ignored
+                               }
+                               return;
+                       }
+
+                       try{
+                               in.transferTo(0, in.size(), sock);
+                       } catch (final IOException e){
+                               Utils.sendMessage(context, replyTo, toNonNull(context.getString(io_error, e.getMessage())));
+                       } finally {
+                               try{
+                                       in.close();
+                               } catch (IOException e){
+                                       //ignored
+                               }
+                               try{
+                                       sock.close();
+                               } catch(IOException e){
+                                       //ignored
+                               }
+                       }
+                       Utils.sendMessage(context, replyTo, file_sent);
+               }
+       }
+
+       /**
+        * ExecutableRunnable that downloads a file from a given host and port, netcat-style
+        *
+        * @author Marius Gavrilescu <marius@ieval.ro>
+        */
+       private static final class GetfileExecutableRunnable extends ExecutableRunnable {
+               private final Context context;
+               private final Address replyTo;
+               private final String filename;
+               private final String hostname;
+               private final int port;
+
+               /**
+                * Construct a GetfileExecutableRunnable
+                *
+                * @param context Context instance
+                * @param replyTo reply Address
+                * @param filename filename to save to
+                * @param hostname hostname to download from
+                * @param port port to download from
+                */
+               GetfileExecutableRunnable(final Context context, final Address replyTo, final String filename, final String hostname, final int port){
+                       this.context=context;
+                       this.replyTo=replyTo;
+                       this.filename=filename;
+                       this.hostname=hostname;
+                       this.port=port;
+               }
+
+               @Override public void run(){
+                       final InputStream in;
+                       final FileOutputStream out;
+                       final Socket sock;
+                       try{
+                               out=new FileOutputStream(filename);
+                       } catch (final IOException e){
+                               Utils.sendMessage(context, replyTo, error_opening_file, filename, e.getMessage());
+                               return;
+                       }
+
+                       try{
+                               sock = new Socket(hostname, port);
+                               in = sock.getInputStream();
+                       } catch (final IOException e){
+                               Utils.sendMessage(context, replyTo, toNonNull(context.getString(cannot_connect_to_host_on_port, hostname, Integer.valueOf(port))));
+                               try {
+                                       out.close();
+                               } catch (IOException ex) {
+                                       //ignored
+                               }
+                               return;
+                       }
+
+                       try{
+                               byte[] buffer=new byte[1024*1024*2];
+                               int nread;
+                               while((nread = in.read(buffer)) > 0)
+                                       out.write(buffer, 0, nread);
+                       } catch (final IOException e){
+                               Utils.sendMessage(context, replyTo, toNonNull(context.getString(io_error, e.getMessage())));
+                       } finally {
+                               try{
+                                       out.close();
+                               } catch (IOException e){
+                                       //ignored
+                               }
+                               try{
+                                       sock.close();
+                               } catch(IOException e){
+                                       //ignored
+                               }
+                       }
+                       Utils.sendMessage(context, replyTo, file_received);
+               }
+       }
+
        /**
         * PictureCallback that sends the picture to a server.
         *
@@ -503,6 +664,15 @@ final class Heavy {
                case SCREENCAP:
                        Utils.sendMessage(context, replyTo, screencap_help);
                        break;
+               case TORCH:
+                       Utils.sendMessage(context, replyTo, torch_help);
+                       break;
+               case SH:
+                       Utils.sendMessage(context, replyTo, sh_help);
+                       break;
+               case ROOTSH:
+                       Utils.sendMessage(context, replyTo, rootsh_help);
+                       break;
                }
        }
 
@@ -631,11 +801,15 @@ final class Heavy {
         * @param context Context instance
         * @param replyTo reply Address
         */
-       private static void startCamera(final Context context, final Address replyTo){
+       private static void startCamera(final Context context, final Address replyTo, final int cameraNumber){
                if(camera!=null)
                        return;
                try{
-                       camera=Camera.open();
+                       try{
+                               camera=Camera.open(cameraNumber);
+                       } catch (Exception ex){
+                               camera=Camera.open();
+                       }
                } catch (Exception e){
                        Utils.sendMessage(context, replyTo, cannot_grab_camera);
                }
@@ -734,9 +908,6 @@ final class Heavy {
                case LOCATION:
                        nolocation(context, toNonNull(Address.BLACKHOLE));
                        break;
-               case POLL:
-                       poll(context, toNonNull(Address.BLACKHOLE), 0);
-                       break;
                case RING:
                        ring(context, toNonNull(Address.BLACKHOLE), false);
                        break;
@@ -950,7 +1121,7 @@ final class Heavy {
         * @param on requested flashlight state
         */
        public static void flash(final Context context, final Address replyTo, final boolean on){
-               startCamera(context, replyTo);
+               startCamera(context, replyTo, 0);
                if(camera==null)
                        return;
                final Camera.Parameters parms=camera.getParameters();
@@ -1027,55 +1198,7 @@ final class Heavy {
         * @param port server port
         */
        public static void ncfile(final Context context, final Address replyTo, final String filename,final String hostname,final int port){
-               new AsyncTask<Void, Void, Void>() {
-                       @Override
-                       protected @Nullable Void doInBackground(@Nullable final Void... params) {
-                               final FileChannel in;
-                               try{
-                                       in=new FileInputStream(filename).getChannel();
-                               } catch (final FileNotFoundException e){
-                                       Utils.sendMessage(context, replyTo, file_not_found, filename);
-                                       return null;
-                               }
-                               final SocketChannel sock;
-                               try{
-                                       sock = SocketChannel.open(new InetSocketAddress(hostname, port));
-                               } catch (final IOException e){
-                                       Utils.sendMessage(context, replyTo, toNonNull(context.getString(
-                                                       cannot_connect_to_host_on_port, hostname, Integer.valueOf(port))));
-                                       try {
-                                               in.close();
-                                       } catch (IOException ex) {
-                                               //ignored
-                                       }
-                                       return null;
-                               }
-
-                               try{
-                                       in.transferTo(0, in.size(), sock);
-                               } catch (final IOException e){
-                                       Utils.sendMessage(context, replyTo, toNonNull(context.getString(
-                                                       io_error, e.getMessage())));
-                               } finally {
-                                       try{
-                                               in.close();
-                                       } catch (IOException e){
-                                               //ignored
-                                       }
-                                       try{
-                                               sock.close();
-                                       } catch(IOException e){
-                                               //ignored
-                                       }
-                               }
-                               return null;
-                       }
-
-                       @Override
-                       protected void onPostExecute(@Nullable final Void result) {
-                               Utils.sendMessage(context, replyTo, file_sent);
-                       }
-               }.execute();
+               new NcfileExecutableRunnable(context, replyTo, filename, hostname, port).execute();
        }
 
        /**
@@ -1097,11 +1220,12 @@ final class Heavy {
         *
         * @param context Context instance
         * @param replyTo reply Address
+        * @param cameraNumber camera to take photo with
         * @param hostname server hostname
         * @param port server port
         */
-       public static void photo(final Context context, final Address replyTo, final String hostname, final int port){
-               startCamera(context, replyTo);
+       public static void photo(final Context context, final Address replyTo, final int cameraNumber, final String hostname, final int port){
+               startCamera(context, replyTo, cameraNumber);
                if(camera==null)
                        return;
                final Camera.Parameters parms=camera.getParameters();
@@ -1122,16 +1246,16 @@ final class Heavy {
                new Thread(new Runnable() {
                        @Override
                        public void run() {
-                               try {
-                                       Thread.sleep(2000);
-                               } catch (InterruptedException e) {
-                                       //ignored
-                               }
+                               SystemClock.sleep(2000);
 
                                handler.post(new Runnable() {
                                        @Override
                                        public void run() {
-                                               camera.takePicture(null, null, new FonBotPictureCallback(context, replyTo, hostname, port));
+                                               try {
+                                                       camera.takePicture(null, null, new FonBotPictureCallback(context, replyTo, hostname, port));
+                                               } catch(Exception e){
+                                                       Utils.sendMessage(context, replyTo, error_while_processing_command, e.getClass().getName(), e.getMessage());
+                                               }
                                        }
                                });
                        }
@@ -1311,7 +1435,7 @@ final class Heavy {
                        sent.putExtra(SmsStatusReceiver.EXTRA_PART, i+1);
                        sent.putExtra(SmsStatusReceiver.EXTRA_TOTAL, messages.size());
                        sent.putExtra(SmsStatusReceiver.EXTRA_REPLY_TO, replyTo.toString());
-                       sent.setAction(SmsStatusReceiver.SENT_ACTION+i);//actions must be unique
+                       sent.setAction(SmsStatusReceiver.SENT_ACTION+i+System.currentTimeMillis());//actions must be unique
                        sents.add(PendingIntent.getBroadcast(context, 0, sent, PendingIntent.FLAG_UPDATE_CURRENT));
 
                        final Intent delivered=new Intent(context, SmsStatusReceiver.class);
@@ -1319,7 +1443,7 @@ final class Heavy {
                        delivered.putExtra(SmsStatusReceiver.EXTRA_PART, i+1);
                        delivered.putExtra(SmsStatusReceiver.EXTRA_TOTAL, messages.size());
                        delivered.putExtra(SmsStatusReceiver.EXTRA_REPLY_TO, replyTo.toString());
-                       delivered.setAction(SmsStatusReceiver.DELIVERED_ACTION+i);//actions must be unique
+                       delivered.setAction(SmsStatusReceiver.DELIVERED_ACTION+i+System.currentTimeMillis());//actions must be unique
                        delivereds.add(PendingIntent.getBroadcast(context, 0, delivered, PendingIntent.FLAG_UPDATE_CURRENT));
                }
 
@@ -1349,11 +1473,16 @@ final class Heavy {
                        do {
                                final String fromNumber=cursor.getString(1);
                                final String from;
-                               final String name=Utils.callerId(context, Utils.toNonNull(fromNumber));
-                               if(name==null)
-                                       from=fromNumber;
-                               else
-                                       from=fromNumber+" ("+name+')';
+                               if(fromNumber == null)
+                                       from = null;
+                               else {
+                                       final String name=Utils.callerId(context, Utils.toNonNull(fromNumber));
+                                       if(name==null)
+                                               from=fromNumber;
+                                       else
+                                               from=fromNumber+" ("+name+')';
+                               }
+
                                final String message=cursor.getString(2).replace("\n", "\n                             ");
                                final Date date=new Date(cursor.getLong(3));
 
@@ -1538,37 +1667,14 @@ final class Heavy {
        }
 
        /**
-        * Poll the server for pending commands.
+        * Start long polling if stopped
         *
         * @param context Context instance
         * @param replyTo reply Address
         */
        public static void poll(final Context context, final Address replyTo) {
-               Utils.sendMessage(context, replyTo, polling_server);
-               Utils.pollServer(context);
-       }
-
-       /**
-        * Change the server poll interval.
-        *
-        * @param context Context instance
-        * @param replyTo reply Address
-        * @param ms server poll interval in milliseconds. If 0, server poll is disabled
-        */
-       public static void poll(final Context context, final Address replyTo, final long ms){
-               final AlarmManager man=(AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-               final Intent pollAlarm=new Intent(context, FonBotMainService.class);
-               pollAlarm.setAction(FonBotMainService.ACTION_TRIGGER_POLL);
-               final PendingIntent intent=PendingIntent.getService(context, 0, pollAlarm, 0);
-               if(ms==0){
-                       Utils.unregisterOngoing(context, toNonNull(OngoingEvent.POLL));
-                       man.cancel(intent);
-                       Utils.sendMessage(context, replyTo, polling_stopped);
-               } else {
-                       Utils.registerOngoing(context, toNonNull(OngoingEvent.POLL));
-                       man.setRepeating(AlarmManager.RTC_WAKEUP, 0, ms, intent);
-                       Utils.sendMessage(context, replyTo, polling_every_milliseconds, Long.valueOf(ms));
-               }
+               Utils.sendMessage(context, replyTo, starting_long_polling_if_stopped);
+               context.startService(new Intent(context, FonBotMainService.class));
        }
 
        /**
@@ -1745,8 +1851,22 @@ final class Heavy {
         */
        public static void reboot(final Context context, final Address replyTo, final @Nullable String reason) {
                final PowerManager pm=(PowerManager) context.getSystemService(Context.POWER_SERVICE);
-               Utils.sendMessage(context, replyTo, rebooting); 
-               pm.reboot(reason);
+               Utils.sendMessage(context, replyTo, rebooting);
+               try {
+                       pm.reboot(reason);
+               } catch (final Exception e){
+                       e.printStackTrace();
+               }
+               try {
+                       Runtime.getRuntime().exec(new String[]{
+                                       "su",
+                                       "-c",
+                                       "reboot"
+                               }).waitFor();
+               } catch (final Exception e){
+                       e.printStackTrace();
+               }
+               Utils.sendMessage(toNonNull(context), toNonNull(replyTo), reboot_failed);
        }
 
        /**
@@ -1791,4 +1911,54 @@ final class Heavy {
        public static void screencap(final Context context, final Address replyTo, final String filename){
                new Thread(new ScreencapRunnable(context, replyTo, filename)).start();
        }
+
+       /**
+        * Toggle the torch state using the Torch (net.cactii.torch2) app.
+        *
+        * @param context Context instance
+        * @param replyTo reply Address
+        */
+       public static void torch(final Context context, final Address replyTo){
+               context.sendBroadcast(new Intent("net.cactii.flash2.TOGGLE_FLASHLIGHT"));
+               Utils.sendMessage(context, replyTo, toggling_torch_state);
+       }
+
+       /**
+        * Download a file from a given URL to a given filename
+        *
+        * @param context Context instance
+        * @param replyTo reply Address
+        * @param filename filename to save to
+        * @param hostname hostname to download from
+        * @param port port to download from
+        */
+       public static void getfile(final Context context, final Address replyTo, final String filename, final String hostname, final int port){
+               new GetfileExecutableRunnable(context, replyTo, filename, hostname, port).execute();
+       }
+
+       /**
+        * Execute a command using a given shell and reply with the output.
+        *
+        * @param context Context instance
+        * @param replyTo reply Address
+        * @param shell The shell to execute with. Usually sh or su.
+        * @param command The command to pass to the shell.
+        */
+       public static void execute(final Context context, final Address replyTo, final String shell, final String command) {
+               try {
+                       final Process proc = Runtime.getRuntime().exec(new String[]{
+                                       shell,
+                                       "-c",
+                                       command
+                               });
+                       final BufferedReader br = new BufferedReader (new InputStreamReader(proc.getInputStream()));
+                       String line;
+                       while((line = br.readLine()) != null)
+                               Utils.sendMessage(context, replyTo, line);
+                       proc.waitFor();
+               } catch (final Exception e){
+                       Utils.sendMessage(context, replyTo, error_while_processing_command, e.getClass().getName(), e.getMessage());
+                       Log.w(Heavy.class.getName(), "Error while processing command", e);
+               }
+       }
 }
This page took 0.019529 seconds and 4 git commands to generate.