package io.scal.secureshareui.controller;
import timber.log.Timber;
import io.scal.secureshareui.login.SSHLoginActivity;
import io.scal.secureshareui.model.Account;
import io.scal.secureshareuilibrary.R;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.util.Log;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.ProxySOCKS4;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
public class SSHSiteController extends SiteController {
private static final String TAG = "SSHSiteController";
public static final String SITE_NAME = "Private Server (SSH)";
public static final String SITE_KEY = "ssh";
public SSHSiteController(Context context, Handler handler, String jobId) {
super(context, handler, jobId);
// TODO Auto-generated constructor stub
}
@Override
public void startAuthentication(Account account) {
Intent intent = new Intent(mContext, SSHLoginActivity.class);
intent.putExtra(SiteController.EXTRAS_KEY_CREDENTIALS, account.getCredentials());
((Activity) mContext).startActivityForResult(intent, SiteController.CONTROLLER_REQUEST_CODE); // FIXME not a safe cast, context might be a service
}
@Override
public void upload(Account account, HashMap<String, String> valueMap) {
Timber.d("Upload file: Entering upload");
String title = valueMap.get("title");
String body = valueMap.get("body");
String mediaPath = valueMap.get("mediaPath");
boolean useTor = (valueMap.get(VALUE_KEY_USE_TOR).equals("true")) ? true : false;
String host = null;
String remotePath = null;
JSONObject obj = null;
try {
obj = new JSONObject(account.getData());
host = obj.getString(SSHLoginActivity.DATA_KEY_SERVER_URL);
} catch (JSONException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// jobFailed(267321323, "No Hostname or IP Address specified for SSH server");
try {
if (obj != null) remotePath = obj.getString(SSHLoginActivity.DATA_KEY_REMOTE_PATH);
} catch (JSONException e) {
// ignore this, its optional
}
String[] chunks = mediaPath.split("/");
String fileName = chunks[chunks.length-1];
String remoteFile = null;
if ((remotePath != null) && (!remotePath.isEmpty())) {
// FIXME check for trailing /
remoteFile = remotePath + "/" + fileName;
} else {
remoteFile = fileName;
}
if (SSH.scpTo(mContext, mediaPath, account.getUserName(), account.getCredentials(), host, remoteFile, this, useTor, mContext)) {
String result = account.getUserName() + "@" + host + ":" + remoteFile;
jobSucceeded(result);
} else {
jobFailed(null, 2767234, "SSH upload failed.");
}
}
public static class SSH {
public static boolean checkCredentials(String username, String password, String host) {
JSch jsch = new JSch();
try {
Session session = jsch.getSession(username, host, 22);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
session.disconnect();
} catch (JSchException e) {
e.printStackTrace();
return false;
}
return true;
}
// public static boolean scpTo(String filePath, String target, final String password, SiteController controller) {
public static boolean scpTo(Context context, String filePath, String username, final String password, String host, String remoteFile, SiteController controller, boolean useTor, Context mContext) {
// if (arg.length != 2) {
// System.err.println("usage: java ScpTo file1 user@remotehost:file2");
// System.exit(-1);
// }
FileInputStream fis = null;
try {
// String user = target.substring(0, target.indexOf('@'));
// target = target.substring(target.indexOf('@') + 1);
// String host = target.substring(0, target.indexOf(':'));
// String remoteFile = target.substring(target.indexOf(':') + 1);
JSch jsch = new JSch();
Session session = jsch.getSession(username, host, 22);
session.setConfig("StrictHostKeyChecking", "no"); // FIXME disabling host ssh checking for now
if (torCheck(useTor, mContext)) {
session.setProxy(new ProxySOCKS4(ORBOT_HOST, ORBOT_SOCKS_PORT));
}
// username and password will be given via UserInfo interface.
UserInfo ui = new UserInfo() {
@Override
public String getPassphrase() {
// TODO Auto-generated method stub
return password;
}
@Override
public String getPassword() {
// TODO Auto-generated method stub
return password;
}
@Override
public boolean promptPassphrase(String arg0) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean promptPassword(String arg0) {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean promptYesNo(String arg0) {
// TODO Auto-generated method stub
return true;
}
@Override
public void showMessage(String arg0) {
// TODO Auto-generated method stub
}
}; //new MyUserInfo();
session.setUserInfo(ui);
session.connect();
boolean ptimestamp = true;
// exec 'scp -t rfile' remotely
String command = "scp " + (ptimestamp ? "-p" : "") + " -t " + remoteFile;
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
// get I/O streams for remote scp
OutputStream out = channel.getOutputStream();
InputStream in = channel.getInputStream();
channel.connect();
if (checkAck(in) != 0) {
return false; // FIXME report the error
}
File _lfile = new File(filePath);
if (ptimestamp) {
command = "T " + (_lfile.lastModified() / 1000) + " 0";
// The access time should be sent here,
// but it is not accessible with JavaAPI ;-<
command += (" " + (_lfile.lastModified() / 1000) + " 0\n");
out.write(command.getBytes());
out.flush();
if (checkAck(in) != 0) {
return false; // FIXME report the error
}
}
// send "C0644 filesize filename", where filename should not
// include '/'
long filesize = _lfile.length();
command = "C0644 " + filesize + " ";
if (filePath.lastIndexOf('/') > 0) {
command += filePath.substring(filePath.lastIndexOf('/') + 1);
}
else {
command += filePath;
}
command += "\n";
out.write(command.getBytes());
out.flush();
if (checkAck(in) != 0) {
return false; // FIXME report the error
}
// send a content of lfile
fis = new FileInputStream(filePath);
byte[] buf = new byte[1024];
int bytesTransfered = 0;
float progress = 0;
int lastProgressRounded = -1;
int progressRounded = 0;
while (true) {
int len = fis.read(buf, 0, buf.length);
if (len <= 0) {
break;
}
bytesTransfered += len;
progress = ((float) bytesTransfered) / ((float) _lfile.length());
progressRounded = Math.round(progress * 100); // rate limit the progress to single percent
if (progressRounded != lastProgressRounded) {
controller.jobProgress(progress, context.getString(R.string.ssh_upload_in_progress));
}
lastProgressRounded = progressRounded;
out.write(buf, 0, len); // out.flush();
}
fis.close();
fis = null;
// send '\0'
buf[0] = 0;
out.write(buf, 0, 1);
out.flush();
if (checkAck(in) != 0) {
return false; // FIXME report the error
}
out.close();
channel.disconnect();
session.disconnect();
return true;
} catch (Exception e) {
System.out.println(e);
try {
if (fis != null)
fis.close();
} catch (Exception ee) {
}
}
return false;
}
static int checkAck(InputStream in) throws IOException {
int b = in.read();
// b may be 0 for success,
// 1 for error,
// 2 for fatal error,
// -1
if (b == 0)
return b;
if (b == -1)
return b;
if (b == 1 || b == 2) {
StringBuffer sb = new StringBuffer();
int c;
do {
c = in.read();
sb.append((char) c);
} while (c != '\n');
if (b == 1) { // error
System.out.print(sb.toString());
}
if (b == 2) { // fatal error
System.out.print(sb.toString());
}
}
return b;
}
// public static class MyUserInfo implements UserInfo, UIKeyboardInteractive {
// public String getPassword() {
// return passwd;
// }
//
// public boolean promptYesNo(String str) {
// Object[] options = {
// "yes", "no"
// };
// int foo = JOptionPane.showOptionDialog(null,
// str,
// "Warning",
// JOptionPane.DEFAULT_OPTION,
// JOptionPane.WARNING_MESSAGE,
// null, options, options[0]);
// return foo == 0;
// }
//
// String passwd;
// JTextField passwordField = (JTextField) new JPasswordField(20);
//
// public String getPassphrase() {
// return null;
// }
//
// public boolean promptPassphrase(String message) {
// return true;
// }
//
// public boolean promptPassword(String message) {
// Object[] ob = {
// passwordField
// };
// int result =
// JOptionPane.showConfirmDialog(null, ob, message,
// JOptionPane.OK_CANCEL_OPTION);
// if (result == JOptionPane.OK_OPTION) {
// passwd = passwordField.getText();
// return true;
// }
// else {
// return false;
// }
// }
//
// public void showMessage(String message) {
// JOptionPane.showMessageDialog(null, message);
// }
//
// final GridBagConstraints gbc =
// new GridBagConstraints(0, 0, 1, 1, 1, 1,
// GridBagConstraints.NORTHWEST,
// GridBagConstraints.NONE,
// new Insets(0, 0, 0, 0), 0, 0);
// private Container panel;
//
// public String[] promptKeyboardInteractive(String destination,
// String name,
// String instruction,
// String[] prompt,
// boolean[] echo) {
// panel = new JPanel();
// panel.setLayout(new GridBagLayout());
//
// gbc.weightx = 1.0;
// gbc.gridwidth = GridBagConstraints.REMAINDER;
// gbc.gridx = 0;
// panel.add(new JLabel(instruction), gbc);
// gbc.gridy++;
//
// gbc.gridwidth = GridBagConstraints.RELATIVE;
//
// JTextField[] texts = new JTextField[prompt.length];
// for (int i = 0; i < prompt.length; i++) {
// gbc.fill = GridBagConstraints.NONE;
// gbc.gridx = 0;
// gbc.weightx = 1;
// panel.add(new JLabel(prompt[i]), gbc);
//
// gbc.gridx = 1;
// gbc.fill = GridBagConstraints.HORIZONTAL;
// gbc.weighty = 1;
// if (echo[i]) {
// texts[i] = new JTextField(20);
// }
// else {
// texts[i] = new JPasswordField(20);
// }
// panel.add(texts[i], gbc);
// gbc.gridy++;
// }
//
// if (JOptionPane.showConfirmDialog(null, panel,
// destination + ": " + name,
// JOptionPane.OK_CANCEL_OPTION,
// JOptionPane.QUESTION_MESSAGE)
// == JOptionPane.OK_OPTION) {
// String[] response = new String[prompt.length];
// for (int i = 0; i < prompt.length; i++) {
// response[i] = texts[i].getText();
// }
// return response;
// }
// else {
// return null; // cancel
// }
// }
// }
}
@Override
public void startMetadataActivity(Intent intent) {
return; // nop
}
}