f1dfc7eee702dda3a44fd4de46975da435645f4d
[dumload.git] / src / com / joshuawise / dumload / Uploader.java
1 package com.joshuawise.dumload;
2
3 import java.io.InputStream;
4 import java.io.OutputStream;
5
6 import com.jcraft.jsch.*;
7 import java.lang.Boolean;
8
9 import android.app.Activity;
10 import android.app.Service;
11 import android.content.Intent;
12 import android.app.PendingIntent;
13 import android.content.Context;
14 import android.net.Uri;
15 import android.os.Bundle;
16 import android.os.IBinder;
17 import android.widget.TextView;
18 import android.widget.Toast;
19 import android.util.Log;
20 import android.app.NotificationManager;
21 import android.app.Notification;
22 import android.os.Handler;
23 import android.os.Messenger;
24 import android.os.Looper;
25 import android.os.Message;
26 import android.os.SystemClock;
27 import android.widget.RemoteViews;
28
29 public class Uploader extends Service implements Runnable, UserInfo, UIKeyboardInteractive {
30         private Uri uri;
31         private String homedir;
32         private Thread me;
33         private static final int HELPME_ID = 1;
34         private RemoteViews remote;
35         private int thenotifid;
36         private Notification thenotif;
37         private String headline;
38         private String dest;
39         
40         private InputStream is;
41         
42         public Object _theObject;
43
44         private void sayNullNotification(final String scroller, final String headline, final String description)
45         {
46                 int bogon = (int)SystemClock.elapsedRealtime();
47                 NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
48                 Notification notification = new Notification(R.drawable.icon, scroller, System.currentTimeMillis());
49
50                 Intent intent = new Intent(this, NotifSlave.class);
51                                         
52                 intent.setAction("com.joshuawise.dumload.NotifSlave");
53                 /* no extras to make the notifslave die */
54                 intent.setData((Uri.parse("suckit://"+SystemClock.elapsedRealtime())));
55                                 
56                 PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0);
57                 notification.defaults |= Notification.DEFAULT_VIBRATE;
58                 notification.flags |= Notification.FLAG_AUTO_CANCEL;
59                 notification.setLatestEventInfo(getApplicationContext(), headline, description, contentIntent);
60                                 
61                 mNotificationManager.notify(bogon, notification);
62         }
63
64         private Object /* pick one type, and fixate on it */ dance(final String type, final String text)        /* for inside the thread */
65         {
66                 final Uploader thisupl = this;
67                 final Message msg = Message.obtain();
68                 
69                 /* t(*A*t) */
70                 Thread t = new Thread() {
71                         public void run() {
72                                 Looper.prepare();
73                                 int bogon = (int)SystemClock.elapsedRealtime();
74                                 
75                                 NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
76                                 Notification notification = new Notification(R.drawable.icon, "Dumload prompt", System.currentTimeMillis());
77                                 
78                                 Handler h = new Handler() {
79                                         public void handleMessage(Message M) {
80                                                 msg.copyFrom(M);
81                                                 Looper.myLooper().quit();
82                                         }
83                                 };
84                                 Messenger m = new Messenger(h);
85                                 
86                                 Intent intent = new Intent(thisupl, NotifSlave.class);
87                                         
88                                 intent.setAction("com.joshuawise.dumload.NotifSlave");
89                                 intent.putExtra("com.joshuawise.dumload.returnmessenger", m);
90                                 intent.putExtra("com.joshuawise.dumload.reqtype", type);
91                                 intent.putExtra("com.joshuawise.dumload.prompt", text);
92                                 intent.setData((Uri.parse("suckit://"+SystemClock.elapsedRealtime())));
93                                 
94                                 PendingIntent contentIntent = PendingIntent.getActivity(thisupl, 0, intent, 0);
95                                 notification.defaults |= Notification.DEFAULT_VIBRATE;
96                                 notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
97                                 notification.setLatestEventInfo(getApplicationContext(), "I've been had!", "Dumload needs your input.", contentIntent);
98                                 
99                                 Log.e("Dumload.Uploader[thread]", "Notifying...");
100                                 
101                                 mNotificationManager.notify(bogon, notification);
102                                 
103                                 Log.e("Dumload.Uploader[thread]", "About to go to 'sleep'...");
104                                 Looper.loop();
105                                 Log.e("Dumload.Uploader[thread]", "And we're alive!");
106                                         
107                                 Log.e("Dumload.Uploader[thread]", "result was: "+(Integer.toString(msg.arg1)));
108                                 
109                                 mNotificationManager.cancel(bogon);
110                         }
111                 };
112                 
113                 t.start();
114                 try {
115                         t.join();
116                 } catch (Exception e) {
117                         return null;
118                 }
119                 
120                 if (type.equals("yesno"))
121                         return new Boolean(msg.arg1 == 1);
122                 else if (type.equals("message"))
123                         return null;
124                 else if (type.equals("password")) {
125                         if (msg.arg1 == 0)
126                                 return null;
127                         Bundle b = msg.getData();
128                         return b.getString("response");
129                 } else
130                         return null;
131         }
132         
133         /* UserInfo bits */
134         String _password = null;
135         public String getPassword()
136         {
137                 return _password;
138         }
139         public boolean promptPassword(String message)
140         {
141                 _password = (String)dance("password", message); 
142                 return (_password != null);
143         }
144         
145         String _passphrase = null;
146         public String getPassphrase()
147         {
148                 return _passphrase;
149         }
150         public boolean promptPassphrase(String message)
151         {
152                 _passphrase = (String)dance("password", message); 
153                 return (_passphrase != null);
154         }
155         
156         public boolean promptYesNo(String str)
157         {
158                 return ((Boolean)dance("yesno", str)).booleanValue();
159         }
160         
161         public void showMessage(String str)
162         {
163                 dance("message", str);
164         }
165         
166         public String[] promptKeyboardInteractive(String dest, String name, String instr, String[] prompt, boolean[] echo)
167         {
168                 int i;
169                 String [] responses = new String[prompt.length];
170                 
171                 Log.e("Dumload.Uploader", "dest: "+dest);
172                 Log.e("Dumload.Uploader", "name: "+name);
173                 Log.e("Dumload.Uploader", "instr: "+instr);
174                 for (i = 0; i < prompt.length; i++)
175                 {
176                         responses[i] = (String) dance("password", "[" + dest + "]\n" + prompt[i]);
177                         if (responses[i] == null)
178                                 return null;
179                 }
180                 return responses;
181         }
182         
183         private void expect_ack(InputStream in) throws Exception, java.io.IOException
184         {
185                 int b = in.read();
186                 
187                 if (b == -1)
188                 {
189                         throw new Exception("unexpected EOF from remote end");
190                 }
191                 
192                 if (b == 1 /* error */ || b == 2 /* fatal error */)
193                 {
194                         StringBuffer sb = new StringBuffer();
195                         int c = 0;
196                         
197                         while ((c = in.read()) != '\n')
198                                 sb.append((char)c);
199                         
200                         throw new Exception("error from remote end: " + sb.toString());
201                 }
202         }
203         
204         private void set_up_notif(final String _headline)
205         {
206                 NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
207                 thenotif = new Notification(R.drawable.icon, headline, System.currentTimeMillis());
208                 thenotifid = (int)SystemClock.elapsedRealtime();
209
210                 Intent intent = new Intent(this, NotifSlave.class);
211                 
212                 headline = _headline;
213                                         
214                 intent.setAction("com.joshuawise.dumload.NotifSlave");
215                 /* no extras to make the notifslave die */
216                 intent.setData((Uri.parse("suckit://"+SystemClock.elapsedRealtime())));
217                                 
218                 PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0);
219                 thenotif.defaults |= 0;
220                 thenotif.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
221                 
222                 remote = new RemoteViews(getPackageName(), R.layout.textnotif);
223                 remote.setImageViewResource(R.id.image, R.drawable.icon);
224                 remote.setTextViewText(R.id.headline, headline);
225                 remote.setTextViewText(R.id.status, "Beginning upload...");
226                 thenotif.contentView = remote;
227                 thenotif.contentIntent = contentIntent;
228                                 
229                 mNotificationManager.notify(thenotifid, thenotif);
230         }
231         
232         private void destroy_notif()
233         {
234                 NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
235                 mNotificationManager.cancel(thenotifid);
236         }
237         
238         private void update_notif(String text)
239         {
240                 NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
241                 
242                 remote = new RemoteViews(getPackageName(), R.layout.textnotif);
243                 remote.setImageViewResource(R.id.image, R.drawable.icon);
244                 remote.setTextViewText(R.id.headline, headline);
245                 remote.setTextViewText(R.id.status, text);
246                 thenotif.contentView = remote;
247                 
248                 mNotificationManager.notify(thenotifid, thenotif);
249         }
250         
251         private void update_notif(int n, int total)
252         {
253                 NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
254                 
255                 remote = new RemoteViews(getPackageName(), R.layout.progressnotif);
256                 remote.setImageViewResource(R.id.image, R.drawable.icon);
257                 remote.setTextViewText(R.id.headline, headline);
258                 remote.setProgressBar(R.id.status, total, n, false);
259                 thenotif.contentView = remote;
260                 
261                 mNotificationManager.notify(thenotifid, thenotif);
262         }
263         
264         @Override
265         public void run()
266         {
267                 Looper.prepare();
268                 
269                 Log.e("Dumload.Uploader[thread]", "This brought to you from the new thread.");
270                 
271                 set_up_notif("Dumload upload: " + dest);
272                 
273                 try {
274                         say("Uploading "+(Integer.toString(is.available()))+" bytes");
275                 
276                         update_notif("Connecting...");
277                 
278                         JSch jsch = new JSch();
279                         jsch.setKnownHosts(homedir + "/known_hosts");
280                         try {
281                                 jsch.addIdentity(homedir + "/id_dsa");
282                         } catch (java.lang.Exception e) {
283                         }
284                         Session s = jsch.getSession("joshua", "nyus.joshuawise.com", 22);
285                         s.setUserInfo(this);
286                         s.connect();
287                         
288                         Channel channel = s.openChannel("exec");
289                         ((ChannelExec)channel).setCommand("scp -t "+dest);
290                         channel.connect();
291                         
292                         OutputStream scp_out = channel.getOutputStream();
293                         InputStream scp_in = channel.getInputStream();
294                         
295                         update_notif("Starting send...");
296                         
297                         /* Okay, BS out of the way.  Now go send the file. */
298                         expect_ack(scp_in);
299                         
300                         String stfu;
301                         if (dest.lastIndexOf("/") > 0)
302                                 stfu = dest.substring(dest.lastIndexOf("/") + 1);
303                         else
304                                 stfu = dest;
305                         
306                         scp_out.write(("C0644 " + (Integer.toString(is.available())) + " "+stfu+"\n").getBytes());
307                         scp_out.flush();
308                         
309                         expect_ack(scp_in);
310                         
311                         int total, nbytes;
312                         total = is.available();
313                         nbytes = 0;
314                         int len;
315                         byte[] buf = new byte[4096];
316                         while ((len = is.read(buf, 0, buf.length)) > 0)
317                         {
318                                 scp_out.write(buf, 0, len);
319                                 nbytes += len;
320                                 update_notif(nbytes, total);
321                         }
322                         
323                         is.close();
324                         
325                         update_notif("Finishing file transfer...");
326                         
327                         scp_out.write("\0".getBytes());
328                         scp_out.flush();
329                         
330                         expect_ack(scp_in);
331                         
332                         channel.disconnect();
333                         
334                         update_notif("Preparing to resize image...");
335                         
336                         channel = s.openChannel("exec");
337                         ((ChannelExec)channel).setCommand("pscale "+dest);
338                         channel.connect();
339                         
340                         scp_in = channel.getInputStream();
341                         
342                         update_notif("Resizing image...");
343                         while ((len = scp_in.read(buf, 0, buf.length)) > 0)
344                                 ;
345                         
346                         channel.disconnect();
347                         update_notif("Upload complete.");
348                         
349                         sayNullNotification("Dumload upload complete: " + dest, "Upload complete", "Uploaded: " + dest);
350
351                         s.disconnect();
352                 } catch (Exception e) {
353                         Log.e("Dumload.uploader[thread]", "JSchException: "+(e.toString()));
354                         sayNullNotification("Dumload upload failed", "Upload failed", e.toString());
355                 }
356                 
357                 destroy_notif();
358                 
359                 Log.e("Dumload.uploader[thread]", "And now I'm back to life!");
360         }
361         
362         private void say(String s) {
363                 Toast.makeText(getApplicationContext(), s, Toast.LENGTH_SHORT).show();
364         }
365         
366         @Override
367         public void onStart(Intent i, int startId)
368         {
369                 uri = i.getData();
370                 dest = i.getStringExtra("com.joshuawise.dumload.dest");
371                 homedir = getApplicationContext().getFilesDir().getAbsolutePath();
372                 int shits = 0;
373                 
374                 super.onStart(i, startId);
375                 
376                 Log.e("Dumload.Uploader", "Started.");
377                 Log.e("Dumload.Uploader", "My path is "+homedir);
378                 
379                 try {
380                         is = getContentResolver().openInputStream(uri);
381                 } catch (Exception e) {
382                         say("Failed to open input file.");
383                         return;
384                 }
385                 
386                 
387                 me = new Thread(this, "Uploader thread");
388                 me.start();
389         }
390         
391         @Override
392         public IBinder onBind(Intent i) {
393                 Log.e("Dumload.Uploader", "bound");
394                 
395                 return null;
396         }
397 }
This page took 0.034653 seconds and 2 git commands to generate.