Merge commit and fix.

This commit is contained in:
Joe Bowser
2012-05-24 09:31:11 -07:00
16 changed files with 1114 additions and 485 deletions
@@ -18,7 +18,11 @@
*/
package org.apache.cordova;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.cordova.api.CordovaInterface;
import org.apache.cordova.api.Plugin;
@@ -31,6 +35,8 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Location;
import android.util.Log;
import android.content.Context;
/**
@@ -43,16 +49,16 @@ public class AccelListener extends Plugin implements SensorEventListener {
public static int STARTING = 1;
public static int RUNNING = 2;
public static int ERROR_FAILED_TO_START = 3;
private float x,y,z; // most recent acceleration values
private long timestamp; // time of most recent value
private int status; // status of listener
private int accuracy = SensorManager.SENSOR_STATUS_UNRELIABLE;
public float TIMEOUT = 30000; // Timeout in msec to shut off listener
private SensorManager sensorManager; // Sensor manager
private Sensor mSensor; // Acceleration sensor returned by sensor manager
float x, y, z; // most recent acceleration values
long timestamp; // time of most recent value
int status; // status of listener
long lastAccessTime; // time the value was last retrieved
private SensorManager sensorManager;// Sensor manager
Sensor mSensor; // Acceleration sensor returned by sensor manager
private String callbackId; // Keeps track of the single "start" callback ID passed in from JS
/**
* Create an accelerometer listener.
@@ -63,8 +69,8 @@ public class AccelListener extends Plugin implements SensorEventListener {
this.z = 0;
this.timestamp = 0;
this.setStatus(AccelListener.STOPPED);
}
}
/**
* Sets the context of the Command. This can then be used to do things like
* get file paths associated with the Activity.
@@ -80,106 +86,36 @@ public class AccelListener extends Plugin implements SensorEventListener {
/**
* Executes the request and returns PluginResult.
*
* @param action The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param callbackId The callback id used when calling back into JavaScript.
* @return A PluginResult object with a status and message.
* @param action The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param callbackId The callback id used when calling back into JavaScript.
* @return A PluginResult object with a status and message.
*/
public PluginResult execute(String action, JSONArray args, String callbackId) {
PluginResult.Status status = PluginResult.Status.OK;
String result = "";
PluginResult.Status status = PluginResult.Status.NO_RESULT;
String message = "";
PluginResult result = new PluginResult(status, message);
result.setKeepCallback(true);
try {
if (action.equals("getStatus")) {
int i = this.getStatus();
return new PluginResult(status, i);
}
else if (action.equals("start")) {
int i = this.start();
return new PluginResult(status, i);
}
else if (action.equals("stop")) {
this.stop();
return new PluginResult(status, 0);
}
else if (action.equals("getAcceleration")) {
if (action.equals("start")) {
this.callbackId = callbackId;
if (this.status != AccelListener.RUNNING) {
// If not running, then this is an async call, so don't worry about waiting
if (this.status != AccelListener.RUNNING) {
int r = this.start();
if (r == AccelListener.ERROR_FAILED_TO_START) {
return new PluginResult(PluginResult.Status.IO_EXCEPTION, AccelListener.ERROR_FAILED_TO_START);
}
// Wait until running
long timeout = 2000;
while ((this.status == STARTING) && (timeout > 0)) {
timeout = timeout - 100;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (timeout == 0) {
return new PluginResult(PluginResult.Status.IO_EXCEPTION, AccelListener.ERROR_FAILED_TO_START);
}
}
this.lastAccessTime = System.currentTimeMillis();
JSONObject r = new JSONObject();
r.put("x", this.x);
r.put("y", this.y);
r.put("z", this.z);
// TODO: Should timestamp be sent?
r.put("timestamp", this.timestamp);
return new PluginResult(status, r);
// We drop the callback onto our stack, call start, and let start and the sensor callback fire off the callback down the road
this.start();
}
else if (action.equals("setTimeout")) {
try {
float timeout = Float.parseFloat(args.getString(0));
this.setTimeout(timeout);
return new PluginResult(status, 0);
} catch (NumberFormatException e) {
status = PluginResult.Status.INVALID_ACTION;
e.printStackTrace();
} catch (JSONException e) {
status = PluginResult.Status.JSON_EXCEPTION;
e.printStackTrace();
}
}
else if (action.equals("getTimeout")) {
float f = this.getTimeout();
return new PluginResult(status, f);
} else {
// Unsupported action
return new PluginResult(PluginResult.Status.INVALID_ACTION);
}
return new PluginResult(status, result);
} catch (JSONException e) {
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
}
}
/**
* Identifies if action to be executed returns a value and should be run synchronously.
*
* @param action The action to execute
* @return T=returns value
*/
public boolean isSynch(String action) {
if (action.equals("getStatus")) {
return true;
}
else if (action.equals("getAcceleration")) {
// Can only return value if RUNNING
else if (action.equals("stop")) {
if (this.status == AccelListener.RUNNING) {
return true;
this.stop();
}
} else {
// Unsupported action
return new PluginResult(PluginResult.Status.INVALID_ACTION);
}
else if (action.equals("getTimeout")) {
return true;
}
return false;
return result;
}
/**
* Called by AccelBroker when listener is to be shut down.
* Stop listener.
@@ -191,46 +127,60 @@ public class AccelListener extends Plugin implements SensorEventListener {
//--------------------------------------------------------------------------
// LOCAL METHODS
//--------------------------------------------------------------------------
//
/**
* Start listening for acceleration sensor.
*
* @return status of listener
* @return status of listener
*/
public int start() {
private int start() {
// If already starting or running, then just return
if ((this.status == AccelListener.RUNNING) || (this.status == AccelListener.STARTING)) {
return this.status;
return this.status;
}
this.setStatus(AccelListener.STARTING);
// Get accelerometer from sensor manager
List<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
// If found, then register as listener
if ((list != null) && (list.size() > 0)) {
this.mSensor = list.get(0);
this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_FASTEST);
this.setStatus(AccelListener.STARTING);
this.lastAccessTime = System.currentTimeMillis();
this.mSensor = list.get(0);
this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_UI);
this.setStatus(AccelListener.STARTING);
} else {
this.setStatus(AccelListener.ERROR_FAILED_TO_START);
this.fail(AccelListener.ERROR_FAILED_TO_START, "No sensors found to register accelerometer listening to.");
return this.status;
}
// If error, then set status to error
else {
this.setStatus(AccelListener.ERROR_FAILED_TO_START);
// Wait until running
long timeout = 2000;
while ((this.status == STARTING) && (timeout > 0)) {
timeout = timeout - 100;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (timeout == 0) {
this.setStatus(AccelListener.ERROR_FAILED_TO_START);
this.fail(AccelListener.ERROR_FAILED_TO_START, "Accelerometer could not be started.");
}
return this.status;
}
/**
* Stop listening to acceleration sensor.
*/
public void stop() {
private void stop() {
if (this.status != AccelListener.STOPPED) {
this.sensorManager.unregisterListener(this);
}
this.setStatus(AccelListener.STOPPED);
this.accuracy = SensorManager.SENSOR_STATUS_UNRELIABLE;
}
/**
@@ -240,6 +190,16 @@ public class AccelListener extends Plugin implements SensorEventListener {
* @param accuracy
*/
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Only look at accelerometer events
if (sensor.getType() != Sensor.TYPE_ACCELEROMETER) {
return;
}
// If not running, then just return
if (this.status == AccelListener.STOPPED) {
return;
}
this.accuracy = accuracy;
}
/**
@@ -248,7 +208,6 @@ public class AccelListener extends Plugin implements SensorEventListener {
* @param SensorEvent event
*/
public void onSensorChanged(SensorEvent event) {
// Only look at accelerometer events
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) {
return;
@@ -258,54 +217,58 @@ public class AccelListener extends Plugin implements SensorEventListener {
if (this.status == AccelListener.STOPPED) {
return;
}
// Save time that event was received
this.timestamp = System.currentTimeMillis();
this.x = event.values[0];
this.y = event.values[1];
this.z = event.values[2];
this.setStatus(AccelListener.RUNNING);
if (this.accuracy >= SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM) {
// If values haven't been read for TIMEOUT time, then turn off accelerometer sensor to save power
if ((this.timestamp - this.lastAccessTime) > this.TIMEOUT) {
this.stop();
// Save time that event was received
this.timestamp = System.currentTimeMillis();
this.x = event.values[0];
this.y = event.values[1];
this.z = event.values[2];
this.win();
}
}
/**
* Get status of accelerometer sensor.
*
* @return status
*/
public int getStatus() {
return this.status;
// Sends an error back to JS
private void fail(int code, String message) {
// Error object
JSONObject errorObj = new JSONObject();
try {
errorObj.put("code", code);
errorObj.put("message", message);
} catch (JSONException e) {
e.printStackTrace();
}
PluginResult err = new PluginResult(PluginResult.Status.ERROR, errorObj);
err.setKeepCallback(true);
this.error(err, this.callbackId);
}
private void win() {
// Success return object
PluginResult result = new PluginResult(PluginResult.Status.OK, this.getAccelerationJSON());
result.setKeepCallback(true);
this.success(result, this.callbackId);
}
/**
* Set the timeout to turn off accelerometer sensor if getX() hasn't been called.
*
* @param timeout Timeout in msec.
*/
public void setTimeout(float timeout) {
this.TIMEOUT = timeout;
}
/**
* Get the timeout to turn off accelerometer sensor if getX() hasn't been called.
*
* @return timeout in msec
*/
public float getTimeout() {
return this.TIMEOUT;
}
/**
* Set the status and send it to JavaScript.
* @param status
*/
private void setStatus(int status) {
this.status = status;
}
private JSONObject getAccelerationJSON() {
JSONObject r = new JSONObject();
try {
r.put("x", this.x);
r.put("y", this.y);
r.put("z", this.z);
r.put("timestamp", this.timestamp);
} catch (JSONException e) {
e.printStackTrace();
}
return r;
}
}
+318 -259
View File
@@ -27,6 +27,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
@@ -35,7 +36,6 @@ import java.util.Iterator;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
@@ -56,7 +56,7 @@ public class FileTransfer extends Plugin {
private static final String LOG_TAG = "FileTransfer";
private static final String LINE_START = "--";
private static final String LINE_END = "\r\n";
private static final String BOUNDRY = "*****";
private static final String BOUNDARY = "*****";
public static int FILE_NOT_FOUND_ERR = 1;
public static int INVALID_URL_ERR = 2;
@@ -80,49 +80,251 @@ public class FileTransfer extends Plugin {
return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing source or target");
}
try {
if (action.equals("upload")) {
// Setup the options
String fileKey = null;
String fileName = null;
String mimeType = null;
if (action.equals("upload")) {
return upload(source, target, args);
} else if (action.equals("download")) {
return download(source, target);
} else {
return new PluginResult(PluginResult.Status.INVALID_ACTION);
}
}
fileKey = getArgument(args, 2, "file");
fileName = getArgument(args, 3, "image.jpg");
mimeType = getArgument(args, 4, "image/jpeg");
JSONObject params = args.optJSONObject(5);
boolean trustEveryone = args.optBoolean(6);
boolean chunkedMode = args.optBoolean(7) || args.isNull(7); //Always use chunked mode unless set to false as per API
FileUploadResult r = upload(source, target, fileKey, fileName, mimeType, params, trustEveryone, chunkedMode);
Log.d(LOG_TAG, "****** About to return a result from upload");
return new PluginResult(PluginResult.Status.OK, r.toJSONObject());
} else if (action.equals("download")) {
JSONObject r = download(source, target);
Log.d(LOG_TAG, "****** About to return a result from download");
return new PluginResult(PluginResult.Status.OK, r);
} else {
return new PluginResult(PluginResult.Status.INVALID_ACTION);
/**
* Uploads the specified file to the server URL provided using an HTTP multipart request.
* @param source Full path of the file on the file system
* @param target URL of the server to receive the file
* @param args JSON Array of args
*
* args[2] fileKey Name of file request parameter
* args[3] fileName File name to be used on server
* args[4] mimeType Describes file content type
* args[5] params key:value pairs of user-defined parameters
* @return FileUploadResult containing result of upload request
*/
private PluginResult upload(String source, String target, JSONArray args) {
Log.d(LOG_TAG, "upload " + source + " to " + target);
HttpURLConnection conn = null;
try {
// Setup the options
String fileKey = getArgument(args, 2, "file");
String fileName = getArgument(args, 3, "image.jpg");
String mimeType = getArgument(args, 4, "image/jpeg");
JSONObject params = args.optJSONObject(5);
if (params == null) params = new JSONObject();
boolean trustEveryone = args.optBoolean(6);
boolean chunkedMode = args.optBoolean(7) || args.isNull(7); //Always use chunked mode unless set to false as per API
Log.d(LOG_TAG, "fileKey: " + fileKey);
Log.d(LOG_TAG, "fileName: " + fileName);
Log.d(LOG_TAG, "mimeType: " + mimeType);
Log.d(LOG_TAG, "params: " + params);
Log.d(LOG_TAG, "trustEveryone: " + trustEveryone);
Log.d(LOG_TAG, "chunkedMode: " + chunkedMode);
// Create return object
FileUploadResult result = new FileUploadResult();
// Get a input stream of the file on the phone
FileInputStream fileInputStream = (FileInputStream) getPathFromUri(source);
DataOutputStream dos = null;
int bytesRead, bytesAvailable, bufferSize;
long totalBytes;
byte[] buffer;
int maxBufferSize = 8096;
//------------------ CLIENT REQUEST
// open a URL connection to the server
URL url = new URL(target);
// Open a HTTP connection to the URL based on protocol
if (url.getProtocol().toLowerCase().equals("https")) {
// Using standard HTTPS connection. Will not allow self signed certificate
if (!trustEveryone) {
conn = (HttpsURLConnection) url.openConnection();
}
// Use our HTTPS connection that blindly trusts everyone.
// This should only be used in debug environments
else {
// Setup the HTTPS connection class to trust everyone
trustAllHosts();
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
// Save the current hostnameVerifier
defaultHostnameVerifier = https.getHostnameVerifier();
// Setup the connection not to verify hostnames
https.setHostnameVerifier(DO_NOT_VERIFY);
conn = https;
}
}
// Return a standard HTTP connection
else {
conn = (HttpURLConnection) url.openConnection();
}
// Allow Inputs
conn.setDoInput(true);
// Allow Outputs
conn.setDoOutput(true);
// Don't use a cached copy.
conn.setUseCaches(false);
// Use a post method.
conn.setRequestMethod("POST");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + BOUNDARY);
// Handle the other headers
try {
JSONObject headers = params.getJSONObject("headers");
for (Iterator iter = headers.keys(); iter.hasNext();)
{
String headerKey = iter.next().toString();
conn.setRequestProperty(headerKey, headers.getString(headerKey));
}
} catch (JSONException e1) {
// No headers to be manipulated!
}
// Set the cookies on the response
String cookie = CookieManager.getInstance().getCookie(target);
if (cookie != null) {
conn.setRequestProperty("Cookie", cookie);
}
/*
* Store the non-file portions of the multipart data as a string, so that we can add it
* to the contentSize, since it is part of the body of the HTTP request.
*/
String extraParams = "";
try {
for (Iterator iter = params.keys(); iter.hasNext();) {
Object key = iter.next();
if(!String.valueOf(key).equals("headers"))
{
extraParams += LINE_START + BOUNDARY + LINE_END;
extraParams += "Content-Disposition: form-data; name=\"" + key.toString() + "\";";
extraParams += LINE_END + LINE_END;
extraParams += params.getString(key.toString());
extraParams += LINE_END;
}
}
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
extraParams += LINE_START + BOUNDARY + LINE_END;
extraParams += "Content-Disposition: form-data; name=\"" + fileKey + "\";" + " filename=\"";
String midParams = "\"" + LINE_END + "Content-Type: " + mimeType + LINE_END + LINE_END;
String tailParams = LINE_END + LINE_START + BOUNDARY + LINE_START + LINE_END;
// Should set this up as an option
if (chunkedMode) {
conn.setChunkedStreamingMode(maxBufferSize);
}
else
{
int stringLength = extraParams.length() + midParams.length() + tailParams.length() + fileName.getBytes("UTF-8").length;
Log.d(LOG_TAG, "String Length: " + stringLength);
int fixedLength = (int) fileInputStream.getChannel().size() + stringLength;
Log.d(LOG_TAG, "Content Length: " + fixedLength);
conn.setFixedLengthStreamingMode(fixedLength);
}
dos = new DataOutputStream( conn.getOutputStream() );
dos.writeBytes(extraParams);
//We don't want to chagne encoding, we just want this to write for all Unicode.
dos.write(fileName.getBytes("UTF-8"));
dos.writeBytes(midParams);
// create a buffer of maximum size
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];
// read file and write it into form...
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
totalBytes = 0;
while (bytesRead > 0) {
totalBytes += bytesRead;
result.setBytesSent(totalBytes);
dos.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
// send multipart form data necesssary after file data...
dos.writeBytes(tailParams);
// close streams
fileInputStream.close();
dos.flush();
dos.close();
//------------------ read the SERVER RESPONSE
StringBuffer responseString = new StringBuffer("");
DataInputStream inStream;
try {
inStream = new DataInputStream ( conn.getInputStream() );
} catch(FileNotFoundException e) {
Log.e(LOG_TAG, e.toString(), e);
throw new IOException("Received error from server");
}
String line;
while (( line = inStream.readLine()) != null) {
responseString.append(line);
}
Log.d(LOG_TAG, "got response from server");
Log.d(LOG_TAG, responseString.toString());
// send request and retrieve response
result.setResponseCode(conn.getResponseCode());
result.setResponse(responseString.toString());
inStream.close();
// Revert back to the proper verifier and socket factories
if (trustEveryone && url.getProtocol().toLowerCase().equals("https")) {
((HttpsURLConnection) conn).setHostnameVerifier(defaultHostnameVerifier);
HttpsURLConnection.setDefaultSSLSocketFactory(defaultSSLSocketFactory);
}
Log.d(LOG_TAG, "****** About to return a result from upload");
return new PluginResult(PluginResult.Status.OK, result.toJSONObject());
} catch (FileNotFoundException e) {
Log.e(LOG_TAG, e.getMessage(), e);
JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target);
JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, conn);
Log.e(LOG_TAG, error.toString(), e);
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} catch (IllegalArgumentException e) {
Log.e(LOG_TAG, e.getMessage(), e);
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target);
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} catch (SSLException e) {
Log.e(LOG_TAG, e.getMessage(), e);
Log.d(LOG_TAG, "Got my ssl exception!!!");
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target);
} catch (MalformedURLException e) {
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, conn);
Log.e(LOG_TAG, error.toString(), e);
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} catch (IOException e) {
Log.e(LOG_TAG, e.getMessage(), e);
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target);
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn);
Log.e(LOG_TAG, error.toString(), e);
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
} catch (Throwable t) {
// Shouldn't happen, but will
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn);
Log.wtf(LOG_TAG, error.toString(), t);
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
@@ -170,18 +372,36 @@ public class FileTransfer extends Plugin {
}
}
/**
* Create an error object based on the passed in errorCode
* @param errorCode the error
* @return JSONObject containing the error
*/
private JSONObject createFileTransferError(int errorCode, String source, String target) {
private JSONObject createFileTransferError(int errorCode, String source, String target, HttpURLConnection connection) {
Integer httpStatus = null;
if (connection != null) {
try {
httpStatus = connection.getResponseCode();
} catch (IOException e) {
Log.w(LOG_TAG, "Error getting HTTP status code from connection.", e);
}
}
return createFileTransferError(errorCode, source, target, httpStatus);
}
/**
* Create an error object based on the passed in errorCode
* @param errorCode the error
* @return JSONObject containing the error
*/
private JSONObject createFileTransferError(int errorCode, String source, String target, Integer httpStatus) {
JSONObject error = null;
try {
error = new JSONObject();
error.put("code", errorCode);
error.put("source", source);
error.put("target", target);
if (httpStatus != null) {
error.put("http_status", httpStatus);
}
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
@@ -206,200 +426,6 @@ public class FileTransfer extends Plugin {
return arg;
}
/**
* Uploads the specified file to the server URL provided using an HTTP
* multipart request.
* @param file Full path of the file on the file system
* @param server URL of the server to receive the file
* @param fileKey Name of file request parameter
* @param fileName File name to be used on server
* @param mimeType Describes file content type
* @param params key:value pairs of user-defined parameters
* @return FileUploadResult containing result of upload request
*/
@SuppressWarnings("deprecation")
public FileUploadResult upload(String file, String server, final String fileKey, final String fileName,
final String mimeType, JSONObject params, boolean trustEveryone, boolean chunkedMode) throws IOException, SSLException {
// Create return object
FileUploadResult result = new FileUploadResult();
// Get a input stream of the file on the phone
FileInputStream fileInputStream = (FileInputStream) getPathFromUri(file);
HttpURLConnection conn = null;
DataOutputStream dos = null;
int bytesRead, bytesAvailable, bufferSize;
long totalBytes;
byte[] buffer;
int maxBufferSize = 8096;
//------------------ CLIENT REQUEST
// open a URL connection to the server
URL url = new URL(server);
// Open a HTTP connection to the URL based on protocol
if (url.getProtocol().toLowerCase().equals("https")) {
// Using standard HTTPS connection. Will not allow self signed certificate
if (!trustEveryone) {
conn = (HttpsURLConnection) url.openConnection();
}
// Use our HTTPS connection that blindly trusts everyone.
// This should only be used in debug environments
else {
// Setup the HTTPS connection class to trust everyone
trustAllHosts();
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
// Save the current hostnameVerifier
defaultHostnameVerifier = https.getHostnameVerifier();
// Setup the connection not to verify hostnames
https.setHostnameVerifier(DO_NOT_VERIFY);
conn = https;
}
}
// Return a standard HTTP connection
else {
conn = (HttpURLConnection) url.openConnection();
}
// Allow Inputs
conn.setDoInput(true);
// Allow Outputs
conn.setDoOutput(true);
// Don't use a cached copy.
conn.setUseCaches(false);
// Use a post method.
conn.setRequestMethod("POST");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + BOUNDRY);
// Handle the other headers
try {
JSONObject headers = params.getJSONObject("headers");
for (@SuppressWarnings("rawtypes")
Iterator iter = headers.keys(); iter.hasNext();)
{
String headerKey = iter.next().toString();
conn.setRequestProperty(headerKey, headers.getString(headerKey));
}
} catch (JSONException e1) {
// No headers to be manipulated!
}
// Set the cookies on the response
String cookie = CookieManager.getInstance().getCookie(server);
if (cookie != null) {
conn.setRequestProperty("Cookie", cookie);
}
/*
* Store the non-file portions of the multipart data as a string, so that we can add it
* to the contentSize, since it is part of the body of the HTTP request.
*/
String extraParams = "";
try {
for (@SuppressWarnings("rawtypes")
Iterator iter = params.keys(); iter.hasNext();) {
Object key = iter.next();
if (key.toString() != "headers")
{
extraParams += LINE_START + BOUNDRY + LINE_END;
extraParams += "Content-Disposition: form-data; name=\"" + key.toString() + "\";";
extraParams += LINE_END + LINE_END;
extraParams += params.getString(key.toString());
extraParams += LINE_END;
}
}
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
extraParams += LINE_START + BOUNDRY + LINE_END;
extraParams += "Content-Disposition: form-data; name=\"" + fileKey + "\";" + " filename=\"";
String midParams = "\"" + LINE_END + "Content-Type: " + mimeType + LINE_END + LINE_END;
String tailParams = LINE_END + LINE_START + BOUNDRY + LINE_START + LINE_END;
// Should set this up as an option
if (chunkedMode) {
conn.setChunkedStreamingMode(maxBufferSize);
}
else
{
int stringLength = extraParams.length() + midParams.length() + tailParams.length() + fileName.getBytes("UTF-8").length;
Log.d(LOG_TAG, "String Length: " + stringLength);
int fixedLength = (int) fileInputStream.getChannel().size() + stringLength;
Log.d(LOG_TAG, "Content Length: " + fixedLength);
conn.setFixedLengthStreamingMode(fixedLength);
}
dos = new DataOutputStream(conn.getOutputStream());
dos.writeBytes(extraParams);
//We don't want to chagne encoding, we just want this to write for all Unicode.
dos.write(fileName.getBytes("UTF-8"));
dos.writeBytes(midParams);
// create a buffer of maximum size
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];
// read file and write it into form...
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
totalBytes = 0;
while (bytesRead > 0) {
totalBytes += bytesRead;
result.setBytesSent(totalBytes);
dos.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
// send multipart form data necesssary after file data...
dos.writeBytes(tailParams);
// close streams
fileInputStream.close();
dos.flush();
dos.close();
//------------------ read the SERVER RESPONSE
StringBuffer responseString = new StringBuffer("");
DataInputStream inStream;
try {
inStream = new DataInputStream(conn.getInputStream());
} catch (FileNotFoundException e) {
throw new IOException("Received error from server");
}
String line;
while ((line = inStream.readLine()) != null) {
responseString.append(line);
}
Log.d(LOG_TAG, "got response from server");
Log.d(LOG_TAG, responseString.toString());
// send request and retrieve response
result.setResponseCode(conn.getResponseCode());
result.setResponse(responseString.toString());
inStream.close();
conn.disconnect();
// Revert back to the proper verifier and socket factories
if (trustEveryone && url.getProtocol().toLowerCase().equals("https")) {
((HttpsURLConnection) conn).setHostnameVerifier(defaultHostnameVerifier);
HttpsURLConnection.setDefaultSSLSocketFactory(defaultSSLSocketFactory);
}
return result;
}
/**
* Downloads a file form a given URL and saves it to the specified directory.
*
@@ -407,7 +433,10 @@ public class FileTransfer extends Plugin {
* @param target Full path of the file on the file system
* @return JSONObject the downloaded file
*/
public JSONObject download(String source, String target) throws IOException {
private PluginResult download(String source, String target) {
Log.d(LOG_TAG, "download " + source + " to " + target);
HttpURLConnection connection = null;
try {
File file = getFileFromPath(target);
@@ -417,16 +446,20 @@ public class FileTransfer extends Plugin {
// connect to server
if (webView.isUrlWhiteListed(source))
{
URL url = new URL(source);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
URL url = new URL(source);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
//Add cookie support
String cookie = CookieManager.getInstance().getCookie(source);
if(cookie != null)
{
connection.setRequestProperty("cookie", cookie);
}
connection.connect();
//Add cookie support
String cookie = CookieManager.getInstance().getCookie(source);
if (cookie != null)
{
connection.setRequestProperty("cookie", cookie);
}
Log.d(LOG_TAG, "Download file: " + url);
connection.connect();
@@ -447,25 +480,42 @@ public class FileTransfer extends Plugin {
Log.d(LOG_TAG, "Saved file: " + target);
// create FileEntry object
FileUtils fileUtil = new FileUtils();
// create FileEntry object
FileUtils fileUtil = new FileUtils();
JSONObject fileEntry = fileUtil.getEntry(file);
return fileUtil.getEntry(file);
return new PluginResult(PluginResult.Status.OK, fileEntry);
}
else
{
throw new IOException("Error: Unable to connect to domain");
Log.w(LOG_TAG, "Source URL is not in white list: '" + source + "'");
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, 401);
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
}
} catch (FileNotFoundException e) {
JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, connection);
Log.e(LOG_TAG, error.toString(), e);
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} catch (MalformedURLException e) {
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, connection);
Log.e(LOG_TAG, error.toString(), e);
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} catch (Exception e) { // IOException, JSONException, NullPointer
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection);
Log.e(LOG_TAG, error.toString(), e);
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} finally {
if (connection != null) {
connection.disconnect();
}
} catch (Exception e) {
Log.d(LOG_TAG, e.getMessage(), e);
throw new IOException("Error while downloading");
}
}
/**
* Get an input stream based on file path or content:// uri
*
* @param path
* @param path foo
* @return an input stream
* @throws FileNotFoundException
*/
@@ -490,14 +540,23 @@ public class FileTransfer extends Plugin {
/**
* Get a File object from the passed in path
*
* @param path
* @return
* @param path file path
* @return file object
*/
private File getFileFromPath(String path) {
if (path.startsWith("file://")) {
return new File(path.substring(7));
private File getFileFromPath(String path) throws FileNotFoundException {
File file;
String prefix = "file://";
if (path.startsWith(prefix)) {
file = new File(path.substring(prefix.length()));
} else {
return new File(path);
file = new File(path);
}
if (file.getParent() == null) {
throw new FileNotFoundException();
}
return file;
}
}