2 * Back-end upload logic for Dumload.
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License, version 3, as
6 * published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 package com.joshuawise.dumload;
19 import java.io.InputStream;
20 import java.io.OutputStream;
22 import com.jcraft.jsch.*;
23 import java.lang.Boolean;
25 import android.app.Activity;
26 import android.app.Service;
27 import android.content.Intent;
28 import android.app.PendingIntent;
29 import android.content.Context;
30 import android.net.Uri;
31 import android.os.Bundle;
32 import android.os.IBinder;
33 import android.widget.TextView;
34 import android.widget.Toast;
35 import android.util.Log;
36 import android.app.NotificationManager;
37 import android.app.Notification;
38 import android.os.Handler;
39 import android.os.Messenger;
40 import android.os.Looper;
41 import android.os.Message;
42 import android.os.SystemClock;
43 import android.widget.RemoteViews;
45 public class Uploader extends Service implements Runnable, UserInfo, UIKeyboardInteractive {
47 private String homedir;
49 private static final int HELPME_ID = 1;
50 private RemoteViews remote;
51 private int thenotifid;
52 private Notification thenotif;
53 private String headline;
56 private InputStream is;
58 public Object _theObject;
60 private void sayNullNotification(final String scroller, final String headline, final String description)
62 int bogon = (int)SystemClock.elapsedRealtime();
63 NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
64 Notification notification = new Notification(R.drawable.icon, scroller, System.currentTimeMillis());
66 Intent intent = new Intent(this, NotifSlave.class);
68 intent.setAction("com.joshuawise.dumload.NotifSlave");
69 /* no extras to make the notifslave die */
70 intent.setData((Uri.parse("suckit://"+SystemClock.elapsedRealtime())));
72 PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0);
73 notification.defaults |= Notification.DEFAULT_VIBRATE;
74 notification.flags |= Notification.FLAG_AUTO_CANCEL;
75 notification.setLatestEventInfo(getApplicationContext(), headline, description, contentIntent);
77 mNotificationManager.notify(bogon, notification);
80 private Object /* pick one type, and fixate on it */ dance(final String type, final String text) /* for inside the thread */
82 final Uploader thisupl = this;
83 final Message msg = Message.obtain();
86 Thread t = new Thread() {
89 int bogon = (int)SystemClock.elapsedRealtime();
91 NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
92 Notification notification = new Notification(R.drawable.icon, "Dumload prompt", System.currentTimeMillis());
94 Handler h = new Handler() {
95 public void handleMessage(Message M) {
97 Looper.myLooper().quit();
100 Messenger m = new Messenger(h);
102 Intent intent = new Intent(thisupl, NotifSlave.class);
104 intent.setAction("com.joshuawise.dumload.NotifSlave");
105 intent.putExtra("com.joshuawise.dumload.returnmessenger", m);
106 intent.putExtra("com.joshuawise.dumload.reqtype", type);
107 intent.putExtra("com.joshuawise.dumload.prompt", text);
108 intent.setData((Uri.parse("suckit://"+SystemClock.elapsedRealtime())));
110 PendingIntent contentIntent = PendingIntent.getActivity(thisupl, 0, intent, 0);
111 notification.defaults |= Notification.DEFAULT_VIBRATE;
112 notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
113 notification.setLatestEventInfo(getApplicationContext(), "I've been had!", "Dumload needs your input.", contentIntent);
115 Log.e("Dumload.Uploader[thread]", "Notifying...");
117 mNotificationManager.notify(bogon, notification);
119 Log.e("Dumload.Uploader[thread]", "About to go to 'sleep'...");
121 Log.e("Dumload.Uploader[thread]", "And we're alive!");
123 Log.e("Dumload.Uploader[thread]", "result was: "+(Integer.toString(msg.arg1)));
125 mNotificationManager.cancel(bogon);
132 } catch (Exception e) {
136 if (type.equals("yesno"))
137 return new Boolean(msg.arg1 == 1);
138 else if (type.equals("message"))
140 else if (type.equals("password")) {
143 Bundle b = msg.getData();
144 return b.getString("response");
150 String _password = null;
151 public String getPassword()
155 public boolean promptPassword(String message)
157 _password = (String)dance("password", message);
158 return (_password != null);
161 String _passphrase = null;
162 public String getPassphrase()
166 public boolean promptPassphrase(String message)
168 _passphrase = (String)dance("password", message);
169 return (_passphrase != null);
172 public boolean promptYesNo(String str)
174 return ((Boolean)dance("yesno", str)).booleanValue();
177 public void showMessage(String str)
179 dance("message", str);
182 public String[] promptKeyboardInteractive(String dest, String name, String instr, String[] prompt, boolean[] echo)
185 String [] responses = new String[prompt.length];
187 Log.e("Dumload.Uploader", "dest: "+dest);
188 Log.e("Dumload.Uploader", "name: "+name);
189 Log.e("Dumload.Uploader", "instr: "+instr);
190 for (i = 0; i < prompt.length; i++)
192 responses[i] = (String) dance("password", "[" + dest + "]\n" + prompt[i]);
193 if (responses[i] == null)
199 private void expect_ack(InputStream in) throws Exception, java.io.IOException
205 throw new Exception("unexpected EOF from remote end");
208 if (b == 1 /* error */ || b == 2 /* fatal error */)
210 StringBuffer sb = new StringBuffer();
213 while ((c = in.read()) != '\n')
216 throw new Exception("error from remote end: " + sb.toString());
220 private void set_up_notif(final String _headline)
222 NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
223 thenotif = new Notification(R.drawable.icon, headline, System.currentTimeMillis());
224 thenotifid = (int)SystemClock.elapsedRealtime();
226 Intent intent = new Intent(this, NotifSlave.class);
228 headline = _headline;
230 intent.setAction("com.joshuawise.dumload.NotifSlave");
231 /* no extras to make the notifslave die */
232 intent.setData((Uri.parse("suckit://"+SystemClock.elapsedRealtime())));
234 PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0);
235 thenotif.defaults |= 0;
236 thenotif.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
238 remote = new RemoteViews(getPackageName(), R.layout.textnotif);
239 remote.setImageViewResource(R.id.image, R.drawable.icon);
240 remote.setTextViewText(R.id.headline, headline);
241 remote.setTextViewText(R.id.status, "Beginning upload...");
242 thenotif.contentView = remote;
243 thenotif.contentIntent = contentIntent;
245 mNotificationManager.notify(thenotifid, thenotif);
248 private void destroy_notif()
250 NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
251 mNotificationManager.cancel(thenotifid);
254 private void update_notif(String text)
256 NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
258 remote = new RemoteViews(getPackageName(), R.layout.textnotif);
259 remote.setImageViewResource(R.id.image, R.drawable.icon);
260 remote.setTextViewText(R.id.headline, headline);
261 remote.setTextViewText(R.id.status, text);
262 thenotif.contentView = remote;
264 mNotificationManager.notify(thenotifid, thenotif);
267 private void update_notif(int n, int total)
269 NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
271 remote = new RemoteViews(getPackageName(), R.layout.progressnotif);
272 remote.setImageViewResource(R.id.image, R.drawable.icon);
273 remote.setTextViewText(R.id.headline, headline);
274 remote.setProgressBar(R.id.status, total, n, false);
275 thenotif.contentView = remote;
277 mNotificationManager.notify(thenotifid, thenotif);
285 Log.e("Dumload.Uploader[thread]", "This brought to you from the new thread.");
287 set_up_notif("Dumload upload: " + dest);
290 say("Uploading "+(Integer.toString(is.available()))+" bytes");
292 update_notif("Connecting...");
294 JSch jsch = new JSch();
295 jsch.setKnownHosts(homedir + "/known_hosts");
297 jsch.addIdentity(homedir + "/id_dsa");
298 } catch (java.lang.Exception e) {
300 Session s = jsch.getSession("joshua", "nyus.joshuawise.com", 22);
304 Channel channel = s.openChannel("exec");
305 ((ChannelExec)channel).setCommand("scp -t "+dest);
308 OutputStream scp_out = channel.getOutputStream();
309 InputStream scp_in = channel.getInputStream();
311 update_notif("Starting send...");
313 /* Okay, BS out of the way. Now go send the file. */
317 if (dest.lastIndexOf("/") > 0)
318 stfu = dest.substring(dest.lastIndexOf("/") + 1);
322 scp_out.write(("C0644 " + (Integer.toString(is.available())) + " "+stfu+"\n").getBytes());
328 total = is.available();
331 byte[] buf = new byte[4096];
332 while ((len = is.read(buf, 0, buf.length)) > 0)
334 scp_out.write(buf, 0, len);
336 update_notif(nbytes, total);
341 update_notif("Finishing file transfer...");
343 scp_out.write("\0".getBytes());
348 channel.disconnect();
350 update_notif("Preparing to resize image...");
352 channel = s.openChannel("exec");
353 ((ChannelExec)channel).setCommand("pscale "+dest);
356 scp_in = channel.getInputStream();
358 update_notif("Resizing image...");
359 while ((len = scp_in.read(buf, 0, buf.length)) > 0)
362 channel.disconnect();
363 update_notif("Upload complete.");
365 sayNullNotification("Dumload upload complete: " + dest, "Upload complete", "Uploaded: " + dest);
368 } catch (Exception e) {
369 Log.e("Dumload.uploader[thread]", "JSchException: "+(e.toString()));
370 sayNullNotification("Dumload upload failed", "Upload failed", e.toString());
375 Log.e("Dumload.uploader[thread]", "And now I'm back to life!");
378 private void say(String s) {
379 Toast.makeText(getApplicationContext(), s, Toast.LENGTH_SHORT).show();
383 public void onStart(Intent i, int startId)
386 dest = i.getStringExtra("com.joshuawise.dumload.dest");
387 homedir = getApplicationContext().getFilesDir().getAbsolutePath();
390 super.onStart(i, startId);
392 Log.e("Dumload.Uploader", "Started.");
393 Log.e("Dumload.Uploader", "My path is "+homedir);
396 is = getContentResolver().openInputStream(uri);
397 } catch (Exception e) {
398 say("Failed to open input file.");
403 me = new Thread(this, "Uploader thread");
408 public IBinder onBind(Intent i) {
409 Log.e("Dumload.Uploader", "bound");