cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ste...@apache.org
Subject [2/9] git commit: Added amazon-fireos platform. Change to use amazon-fireos as the platform if the user agen string contains 'cordova-amazon-fireos'
Date Thu, 05 Dec 2013 00:58:16 GMT
Added amazon-fireos platform.
Change to use amazon-fireos as the platform if the user agen string contains 'cordova-amazon-fireos'


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/commit/a80ae041
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/tree/a80ae041
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/diff/a80ae041

Branch: refs/heads/master
Commit: a80ae0417b45052c8e2064f605c7027afcadcd06
Parents: 60f96e0
Author: Archana Naik <naika@lab126.com>
Authored: Fri Oct 18 14:44:05 2013 -0700
Committer: Archana Naik <naika@lab126.com>
Committed: Wed Oct 30 13:20:08 2013 -0700

----------------------------------------------------------------------
 plugin.xml                   |  17 +
 src/amazon/FileTransfer.java | 873 ++++++++++++++++++++++++++++++++++++++
 test/cordova-incl.js         |   3 +-
 3 files changed, 892 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/blob/a80ae041/plugin.xml
----------------------------------------------------------------------
diff --git a/plugin.xml b/plugin.xml
index d1de8dd..8d6bafe 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -37,6 +37,23 @@
         <source-file src="src/android/FileUploadResult.java" target-dir="src/org/apache/cordova/filetransfer"
/>
     </platform>
 
+    <!-- amamzon-fireos -->
+    <platform name="amazon-fireos">
+        <config-file target="res/xml/config.xml" parent="/*">
+            <feature name="FileTransfer" >
+                <param name="android-package" value="org.apache.cordova.filetransfer.FileTransfer"/>
+            </feature>
+        </config-file>
+
+        <config-file target="AndroidManifest.xml" parent="/*">
+            <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
/>
+        </config-file>
+
+        <source-file src="src/amazon/FileTransfer.java" target-dir="src/org/apache/cordova/filetransfer"
/>
+        <source-file src="src/android/FileProgressResult.java" target-dir="src/org/apache/cordova/filetransfer"
/>
+        <source-file src="src/android/FileUploadResult.java" target-dir="src/org/apache/cordova/filetransfer"
/>
+    </platform>
+    
     <platform name="blackberry10">
         <config-file target="www/config.xml" parent="/widget">
             <feature name="FileTransfer" value="FileTransfer"></feature>

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/blob/a80ae041/src/amazon/FileTransfer.java
----------------------------------------------------------------------
diff --git a/src/amazon/FileTransfer.java b/src/amazon/FileTransfer.java
new file mode 100644
index 0000000..2e0c31c
--- /dev/null
+++ b/src/amazon/FileTransfer.java
@@ -0,0 +1,873 @@
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+*/
+package org.apache.cordova.filetransfer;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.URLConnection;
+import java.net.URLDecoder;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.Inflater;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.cordova.Config;
+import org.apache.cordova.CallbackContext;
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.CordovaResourceApi;
+import org.apache.cordova.CordovaResourceApi.OpenForReadResult;
+import org.apache.cordova.PluginResult;
+import org.apache.cordova.file.FileUtils;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.net.Uri;
+import android.os.Build;
+import android.util.Log;
+import com.amazon.android.webkit.AmazonCookieManager;
+
+public class FileTransfer extends CordovaPlugin {
+
+    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 BOUNDARY =  "+++++";
+
+    public static int FILE_NOT_FOUND_ERR = 1;
+    public static int INVALID_URL_ERR = 2;
+    public static int CONNECTION_ERR = 3;
+    public static int ABORTED_ERR = 4;
+
+    private static HashMap<String, RequestContext> activeRequests = new HashMap<String,
RequestContext>();
+    private static final int MAX_BUFFER_SIZE = 16 * 1024;
+
+    private static final class RequestContext {
+        String source;
+        String target;
+        File targetFile;
+        CallbackContext callbackContext;
+        InputStream currentInputStream;
+        OutputStream currentOutputStream;
+        boolean aborted;
+        RequestContext(String source, String target, CallbackContext callbackContext) {
+            this.source = source;
+            this.target = target;
+            this.callbackContext = callbackContext;
+        }
+        void sendPluginResult(PluginResult pluginResult) {
+            synchronized (this) {
+                if (!aborted) {
+                    callbackContext.sendPluginResult(pluginResult);
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds an interface method to an InputStream to return the number of bytes
+     * read from the raw stream. This is used to track total progress against
+     * the HTTP Content-Length header value from the server.
+     */
+    private static abstract class TrackingInputStream extends FilterInputStream {
+      public TrackingInputStream(final InputStream in) {
+        super(in);
+      }
+        public abstract long getTotalRawBytesRead();
+  }
+
+    private static class ExposedGZIPInputStream extends GZIPInputStream {
+      public ExposedGZIPInputStream(final InputStream in) throws IOException {
+        super(in);
+      }
+      public Inflater getInflater() {
+        return inf;
+      }
+  }
+
+    /**
+     * Provides raw bytes-read tracking for a GZIP input stream. Reports the
+     * total number of compressed bytes read from the input, rather than the
+     * number of uncompressed bytes.
+     */
+    private static class TrackingGZIPInputStream extends TrackingInputStream {
+      private ExposedGZIPInputStream gzin;
+      public TrackingGZIPInputStream(final ExposedGZIPInputStream gzin) throws IOException
{
+        super(gzin);
+        this.gzin = gzin;
+      }
+      public long getTotalRawBytesRead() {
+        return gzin.getInflater().getBytesRead();
+      }
+  }
+
+    /**
+     * Provides simple total-bytes-read tracking for an existing InputStream
+     */
+    private static class SimpleTrackingInputStream extends TrackingInputStream {
+        private long bytesRead = 0;
+        public SimpleTrackingInputStream(InputStream stream) {
+            super(stream);
+        }
+
+        private int updateBytesRead(int newBytesRead) {
+          if (newBytesRead != -1) {
+            bytesRead += newBytesRead;
+          }
+          return newBytesRead;
+        }
+
+        @Override
+        public int read() throws IOException {
+            return updateBytesRead(super.read());
+        }
+
+        @Override
+        public int read(byte[] buffer) throws IOException {
+            return updateBytesRead(super.read(buffer));
+        }
+
+        @Override
+        public int read(byte[] bytes, int offset, int count) throws IOException {
+            return updateBytesRead(super.read(bytes, offset, count));
+        }
+
+        public long getTotalRawBytesRead() {
+          return bytesRead;
+        }
+    }
+
+    @Override
+    public boolean execute(String action, JSONArray args, final CallbackContext callbackContext)
throws JSONException {
+        if (action.equals("upload") || action.equals("download")) {
+            String source = args.getString(0);
+            String target = args.getString(1);
+
+            if (action.equals("upload")) {
+                try {
+                    source = URLDecoder.decode(source, "UTF-8");
+                    upload(source, target, args, callbackContext);
+                } catch (UnsupportedEncodingException e) {
+                    callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.MALFORMED_URL_EXCEPTION,
"UTF-8 error."));
+                }
+            } else {
+                download(source, target, args, callbackContext);
+            }
+            return true;
+        } else if (action.equals("abort")) {
+            String objectId = args.getString(0);
+            abort(objectId);
+            callbackContext.success();
+            return true;
+        }
+        return false;
+    }
+
+    private static void addHeadersToRequest(URLConnection connection, JSONObject headers)
{
+        try {
+            for (Iterator<?> iter = headers.keys(); iter.hasNext(); ) {
+                String headerKey = iter.next().toString();
+                JSONArray headerValues = headers.optJSONArray(headerKey);
+                if (headerValues == null) {
+                    headerValues = new JSONArray();
+                    headerValues.put(headers.getString(headerKey));
+                }
+                connection.setRequestProperty(headerKey, headerValues.getString(0));
+                for (int i = 1; i < headerValues.length(); ++i) {
+                    connection.addRequestProperty(headerKey, headerValues.getString(i));
+                }
+            }
+        } catch (JSONException e1) {
+          // No headers to be manipulated!
+        }
+    }
+
+    /**
+     * 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
+     * @param callbackContext    callback id for optional progress reports
+     *
+     * 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 void upload(final String source, final String target, JSONArray args, CallbackContext
callbackContext) throws JSONException {
+        Log.d(LOG_TAG, "upload " + source + " to " +  target);
+
+        // Setup the options
+        final String fileKey = getArgument(args, 2, "file");
+        final String fileName = getArgument(args, 3, "image.jpg");
+        final String mimeType = getArgument(args, 4, "image/jpeg");
+        final JSONObject params = args.optJSONObject(5) == null ? new JSONObject() : args.optJSONObject(5);
+        final boolean trustEveryone = args.optBoolean(6);
+        // Always use chunked mode unless set to false as per API
+        final boolean chunkedMode = args.optBoolean(7) || args.isNull(7);
+        // Look for headers on the params map for backwards compatibility with older Cordova
versions.
+        final JSONObject headers = args.optJSONObject(8) == null ? params.optJSONObject("headers")
: args.optJSONObject(8);
+        final String objectId = args.getString(9);
+        final String httpMethod = getArgument(args, 10, "POST");
+        
+        final CordovaResourceApi resourceApi = webView.getResourceApi();
+
+        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);
+        Log.d(LOG_TAG, "headers: " + headers);
+        Log.d(LOG_TAG, "objectId: " + objectId);
+        Log.d(LOG_TAG, "httpMethod: " + httpMethod);
+        
+        final Uri targetUri = resourceApi.remapUri(Uri.parse(target));
+        // Accept a path or a URI for the source.
+        Uri tmpSrc = Uri.parse(source);
+        final Uri sourceUri = resourceApi.remapUri(
+            tmpSrc.getScheme() != null ? tmpSrc : Uri.fromFile(new File(source)));
+
+        int uriType = CordovaResourceApi.getUriType(targetUri);
+        final boolean useHttps = uriType == CordovaResourceApi.URI_TYPE_HTTPS;
+        if (uriType != CordovaResourceApi.URI_TYPE_HTTP && !useHttps) {
+            JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null,
0);
+            Log.e(LOG_TAG, "Unsupported URI: " + targetUri);
+            callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION,
error));
+            return;
+        }
+
+        final RequestContext context = new RequestContext(source, target, callbackContext);
+        synchronized (activeRequests) {
+            activeRequests.put(objectId, context);
+        }
+        
+        cordova.getThreadPool().execute(new Runnable() {
+            public void run() {
+                if (context.aborted) {
+                    return;
+                }
+                HttpURLConnection conn = null;
+                HostnameVerifier oldHostnameVerifier = null;
+                SSLSocketFactory oldSocketFactory = null;
+                int totalBytes = 0;
+                int fixedLength = -1;
+                try {
+                    // Create return object
+                    FileUploadResult result = new FileUploadResult();
+                    FileProgressResult progress = new FileProgressResult();
+
+                    //------------------ CLIENT REQUEST
+                    // Open a HTTP connection to the URL based on protocol
+                    conn = resourceApi.createHttpConnection(targetUri);
+                    if (useHttps && trustEveryone) {
+                        // Setup the HTTPS connection class to trust everyone
+                        HttpsURLConnection https = (HttpsURLConnection)conn;
+                        oldSocketFactory  = trustAllHosts(https);
+                        // Save the current hostnameVerifier
+                        oldHostnameVerifier = https.getHostnameVerifier();
+                        // Setup the connection not to verify hostnames
+                        https.setHostnameVerifier(DO_NOT_VERIFY);
+                    }
+
+                    // 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(httpMethod);
+                    conn.setRequestProperty("Content-Type", "multipart/form-data;boundary="
+ BOUNDARY);
+
+                    // Set the cookies on the response
+                    String cookie = AmazonCookieManager.getInstance().getCookie(target);
+                    if (cookie != null) {
+                        conn.setRequestProperty("Cookie", cookie);
+                    }
+
+                    // Handle the other headers
+                    if (headers != null) {
+                        addHeadersToRequest(conn, headers);
+                    }
+
+                    /*
+                        * 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.
+                        */
+                    StringBuilder beforeData = new StringBuilder();
+                    try {
+                        for (Iterator<?> iter = params.keys(); iter.hasNext();) {
+                            Object key = iter.next();
+                            if(!String.valueOf(key).equals("headers"))
+                            {
+                              beforeData.append(LINE_START).append(BOUNDARY).append(LINE_END);
+                              beforeData.append("Content-Disposition: form-data; name=\"").append(key.toString()).append('"');
+                              beforeData.append(LINE_END).append(LINE_END);
+                              beforeData.append(params.getString(key.toString()));
+                              beforeData.append(LINE_END);
+                            }
+                        }
+                    } catch (JSONException e) {
+                        Log.e(LOG_TAG, e.getMessage(), e);
+                    }
+
+                    beforeData.append(LINE_START).append(BOUNDARY).append(LINE_END);
+                    beforeData.append("Content-Disposition: form-data; name=\"").append(fileKey).append("\";");
+                    beforeData.append(" filename=\"").append(fileName).append('"').append(LINE_END);
+                    beforeData.append("Content-Type: ").append(mimeType).append(LINE_END).append(LINE_END);
+                    byte[] beforeDataBytes = beforeData.toString().getBytes("UTF-8");
+                    byte[] tailParamsBytes = (LINE_END + LINE_START + BOUNDARY + LINE_START
+ LINE_END).getBytes("UTF-8");
+
+                    
+                    // Get a input stream of the file on the phone
+                    OpenForReadResult readResult = resourceApi.openForRead(sourceUri);
+                    
+                    int stringLength = beforeDataBytes.length + tailParamsBytes.length;
+                    if (readResult.length >= 0) {
+                        fixedLength = (int)readResult.length + stringLength;
+                        progress.setLengthComputable(true);
+                        progress.setTotal(fixedLength);
+                    }
+                    Log.d(LOG_TAG, "Content Length: " + fixedLength);
+                    // setFixedLengthStreamingMode causes and OutOfMemoryException on pre-Froyo
devices.
+                    // http://code.google.com/p/android/issues/detail?id=3164
+                    // It also causes OOM if HTTPS is used, even on newer devices.
+                    boolean useChunkedMode = chunkedMode && (Build.VERSION.SDK_INT
< Build.VERSION_CODES.FROYO || useHttps);
+                    useChunkedMode = useChunkedMode || (fixedLength == -1);
+
+                    if (useChunkedMode) {
+                        conn.setChunkedStreamingMode(MAX_BUFFER_SIZE);
+                        // Although setChunkedStreamingMode sets this header, setting it
explicitly here works
+                        // around an OutOfMemoryException when using https.
+                        conn.setRequestProperty("Transfer-Encoding", "chunked");
+                    } else {
+                        conn.setFixedLengthStreamingMode(fixedLength);
+                    }
+
+                    conn.connect();
+                    
+                    OutputStream sendStream = null;
+                    try {
+                        sendStream = conn.getOutputStream();
+                        synchronized (context) {
+                            if (context.aborted) {
+                                return;
+                            }
+                            context.currentOutputStream = sendStream;
+                        }
+                        //We don't want to change encoding, we just want this to write for
all Unicode.
+                        sendStream.write(beforeDataBytes);
+                        totalBytes += beforeDataBytes.length;
+    
+                        // create a buffer of maximum size
+                        int bytesAvailable = readResult.inputStream.available();
+                        int bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE);
+                        byte[] buffer = new byte[bufferSize];
+    
+                        // read file and write it into form...
+                        int bytesRead = readResult.inputStream.read(buffer, 0, bufferSize);
+    
+                        long prevBytesRead = 0;
+                        while (bytesRead > 0) {
+                            result.setBytesSent(totalBytes);
+                            sendStream.write(buffer, 0, bytesRead);
+                            totalBytes += bytesRead;
+                            if (totalBytes > prevBytesRead + 102400) {
+                                prevBytesRead = totalBytes;
+                                Log.d(LOG_TAG, "Uploaded " + totalBytes + " of " + fixedLength
+ " bytes");
+                            }
+                            bytesAvailable = readResult.inputStream.available();
+                            bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE);
+                            bytesRead = readResult.inputStream.read(buffer, 0, bufferSize);
+
+                            // Send a progress event.
+                            progress.setLoaded(totalBytes);
+                            PluginResult progressResult = new PluginResult(PluginResult.Status.OK,
progress.toJSONObject());
+                            progressResult.setKeepCallback(true);
+                            context.sendPluginResult(progressResult);
+                        }
+    
+                        // send multipart form data necessary after file data...
+                        sendStream.write(tailParamsBytes);
+                        totalBytes += tailParamsBytes.length;
+                        sendStream.flush();
+                    } finally {
+                        safeClose(readResult.inputStream);
+                        safeClose(sendStream);
+                    }
+                    context.currentOutputStream = null;
+                    Log.d(LOG_TAG, "Sent " + totalBytes + " of " + fixedLength);
+
+                    //------------------ read the SERVER RESPONSE
+                    String responseString;
+                    int responseCode = conn.getResponseCode();
+                    Log.d(LOG_TAG, "response code: " + responseCode);
+                    Log.d(LOG_TAG, "response headers: " + conn.getHeaderFields());
+                    TrackingInputStream inStream = null;
+                    try {
+                        inStream = getInputStream(conn);
+                        synchronized (context) {
+                            if (context.aborted) {
+                                return;
+                            }
+                            context.currentInputStream = inStream;
+                        }
+                        
+                        ByteArrayOutputStream out = new ByteArrayOutputStream(Math.max(1024,
conn.getContentLength()));
+                        byte[] buffer = new byte[1024];
+                        int bytesRead = 0;
+                        // write bytes to file
+                        while ((bytesRead = inStream.read(buffer)) > 0) {
+                            out.write(buffer, 0, bytesRead);
+                        }
+                        responseString = out.toString("UTF-8");
+                    } finally {
+                        context.currentInputStream = null;
+                        safeClose(inStream);
+                    }
+                    
+                    Log.d(LOG_TAG, "got response from server");
+                    Log.d(LOG_TAG, responseString.substring(0, Math.min(256, responseString.length())));
+                    
+                    // send request and retrieve response
+                    result.setResponseCode(responseCode);
+                    result.setResponse(responseString);
+
+                    context.sendPluginResult(new PluginResult(PluginResult.Status.OK, result.toJSONObject()));
+                } catch (FileNotFoundException e) {
+                    JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source,
target, conn);
+                    Log.e(LOG_TAG, error.toString(), e);
+                    context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION,
error));
+                } catch (IOException e) {
+                    JSONObject error = createFileTransferError(CONNECTION_ERR, source, target,
conn);
+                    Log.e(LOG_TAG, error.toString(), e);
+                    Log.e(LOG_TAG, "Failed after uploading " + totalBytes + " of " + fixedLength
+ " bytes.");
+                    context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION,
error));
+                } catch (JSONException e) {
+                    Log.e(LOG_TAG, e.getMessage(), e);
+                    context.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+                } catch (Throwable t) {
+                    // Shouldn't happen, but will
+                    JSONObject error = createFileTransferError(CONNECTION_ERR, source, target,
conn);
+                    Log.e(LOG_TAG, error.toString(), t);
+                    context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION,
error));
+                } finally {
+                    synchronized (activeRequests) {
+                        activeRequests.remove(objectId);
+                    }
+
+                    if (conn != null) {
+                        // Revert back to the proper verifier and socket factories
+                        // Revert back to the proper verifier and socket factories
+                        if (trustEveryone && useHttps) {
+                            HttpsURLConnection https = (HttpsURLConnection) conn;
+                            https.setHostnameVerifier(oldHostnameVerifier);
+                            https.setSSLSocketFactory(oldSocketFactory);
+                        }
+                    }
+                }                
+            }
+        });
+    }
+
+    private static void safeClose(Closeable stream) {
+        if (stream != null) {
+            try {
+                stream.close();
+            } catch (IOException e) {
+            }
+        }
+    }
+
+    private static TrackingInputStream getInputStream(URLConnection conn) throws IOException
{
+        String encoding = conn.getContentEncoding();
+        if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
+          return new TrackingGZIPInputStream(new ExposedGZIPInputStream(conn.getInputStream()));
+        }
+        return new SimpleTrackingInputStream(conn.getInputStream());
+    }
+
+    // always verify the host - don't check for certificate
+    private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
+        public boolean verify(String hostname, SSLSession session) {
+            return true;
+        }
+    };
+    // Create a trust manager that does not validate certificate chains
+    private static final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager()
{
+        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+            return new java.security.cert.X509Certificate[] {};
+        }
+        
+        public void checkClientTrusted(X509Certificate[] chain,
+                String authType) throws CertificateException {
+        }
+        
+        public void checkServerTrusted(X509Certificate[] chain,
+                String authType) throws CertificateException {
+        }
+    } };
+
+    /**
+     * This function will install a trust manager that will blindly trust all SSL
+     * certificates.  The reason this code is being added is to enable developers
+     * to do development using self signed SSL certificates on their web server.
+     *
+     * The standard HttpsURLConnection class will throw an exception on self
+     * signed certificates if this code is not run.
+     */
+    private static SSLSocketFactory trustAllHosts(HttpsURLConnection connection) {
+        // Install the all-trusting trust manager
+        SSLSocketFactory oldFactory = connection.getSSLSocketFactory();
+        try {
+            // Install our all trusting manager
+            SSLContext sc = SSLContext.getInstance("TLS");
+            sc.init(null, trustAllCerts, new java.security.SecureRandom());
+            SSLSocketFactory newFactory = sc.getSocketFactory();
+            connection.setSSLSocketFactory(newFactory);
+        } catch (Exception e) {
+            Log.e(LOG_TAG, e.getMessage(), e);
+        }
+        return oldFactory;
+    }
+
+    private static JSONObject createFileTransferError(int errorCode, String source, String
target, URLConnection connection) {
+
+        int httpStatus = 0;
+        StringBuilder bodyBuilder = new StringBuilder();
+        String body = null;
+        if (connection != null) {
+            try {
+                if (connection instanceof HttpURLConnection) {
+                    httpStatus = ((HttpURLConnection)connection).getResponseCode();
+                    InputStream err = ((HttpURLConnection) connection).getErrorStream();
+                    if(err != null)
+                    {
+                        BufferedReader reader = new BufferedReader(new InputStreamReader(err,
"UTF-8"));
+                        String line = reader.readLine();
+                        while(line != null)
+                        {
+                            bodyBuilder.append(line);
+                            line = reader.readLine();
+                            if(line != null)
+                                bodyBuilder.append('\n');
+                        }
+                        body = bodyBuilder.toString();
+                    }
+                }
+            // IOException can leave connection object in a bad state, so catch all exceptions.
+            } catch (Throwable e) {
+                Log.w(LOG_TAG, "Error getting HTTP status code from connection.", e);
+            }
+        }
+
+        return createFileTransferError(errorCode, source, target, body, httpStatus);
+    }
+
+        /**
+        * Create an error object based on the passed in errorCode
+        * @param errorCode      the error
+        * @return JSONObject containing the error
+        */
+    private static JSONObject createFileTransferError(int errorCode, String source, String
target, String body, Integer httpStatus) {
+        JSONObject error = null;
+        try {
+            error = new JSONObject();
+            error.put("code", errorCode);
+            error.put("source", source);
+            error.put("target", target);
+            if(body != null)
+            {
+                error.put("body", body);
+            }   
+            if (httpStatus != null) {
+                error.put("http_status", httpStatus);
+            }
+        } catch (JSONException e) {
+            Log.e(LOG_TAG, e.getMessage(), e);
+        }
+        return error;
+    }
+
+    /**
+     * Convenience method to read a parameter from the list of JSON args.
+     * @param args                      the args passed to the Plugin
+     * @param position          the position to retrieve the arg from
+     * @param defaultString the default to be used if the arg does not exist
+     * @return String with the retrieved value
+     */
+    private static String getArgument(JSONArray args, int position, String defaultString)
{
+        String arg = defaultString;
+        if (args.length() > position) {
+            arg = args.optString(position);
+            if (arg == null || "null".equals(arg)) {
+                arg = defaultString;
+            }
+        }
+        return arg;
+    }
+
+    /**
+     * Downloads a file form a given URL and saves it to the specified directory.
+     *
+     * @param source        URL of the server to receive the file
+     * @param target            Full path of the file on the file system
+     */
+    private void download(final String source, final String target, JSONArray args, CallbackContext
callbackContext) throws JSONException {
+        Log.d(LOG_TAG, "download " + source + " to " +  target);
+
+        final CordovaResourceApi resourceApi = webView.getResourceApi();
+
+        final boolean trustEveryone = args.optBoolean(2);
+        final String objectId = args.getString(3);
+        final JSONObject headers = args.optJSONObject(4);
+        
+        final Uri sourceUri = resourceApi.remapUri(Uri.parse(source));
+        // Accept a path or a URI for the source.
+        Uri tmpTarget = Uri.parse(target);
+        final Uri targetUri = resourceApi.remapUri(
+            tmpTarget.getScheme() != null ? tmpTarget : Uri.fromFile(new File(target)));
+
+        int uriType = CordovaResourceApi.getUriType(sourceUri);
+        final boolean useHttps = uriType == CordovaResourceApi.URI_TYPE_HTTPS;
+        final boolean isLocalTransfer = !useHttps && uriType != CordovaResourceApi.URI_TYPE_HTTP;
+        if (uriType == CordovaResourceApi.URI_TYPE_UNKNOWN) {
+            JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null,
0);
+            Log.e(LOG_TAG, "Unsupported URI: " + targetUri);
+            callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION,
error));
+            return;
+        }
+        
+        // TODO: refactor to also allow resources & content:
+        if (!isLocalTransfer && !Config.isUrlWhiteListed(source)) {
+            Log.w(LOG_TAG, "Source URL is not in white list: '" + source + "'");
+            JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, null,
401);
+            callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION,
error));
+            return;
+        }
+
+        
+        final RequestContext context = new RequestContext(source, target, callbackContext);
+        synchronized (activeRequests) {
+            activeRequests.put(objectId, context);
+        }
+        
+        cordova.getThreadPool().execute(new Runnable() {
+            public void run() {
+                if (context.aborted) {
+                    return;
+                }
+                HttpURLConnection connection = null;
+                HostnameVerifier oldHostnameVerifier = null;
+                SSLSocketFactory oldSocketFactory = null;
+                File file = null;
+                PluginResult result = null;
+                TrackingInputStream inputStream = null;
+
+                OutputStream outputStream = null;
+                try {
+                    OpenForReadResult readResult = null;
+                    outputStream = resourceApi.openOutputStream(targetUri);
+
+                    file = resourceApi.mapUriToFile(targetUri);
+                    context.targetFile = file;
+                    
+                    Log.d(LOG_TAG, "Download file:" + sourceUri);
+
+                    FileProgressResult progress = new FileProgressResult();
+
+                    if (isLocalTransfer) {
+                        readResult = resourceApi.openForRead(sourceUri);
+                        if (readResult.length != -1) {
+                            progress.setLengthComputable(true);
+                            progress.setTotal(readResult.length);
+                        }
+                        inputStream = new SimpleTrackingInputStream(readResult.inputStream);
+                    } else {
+                        // connect to server
+                        // Open a HTTP connection to the URL based on protocol
+                        connection = resourceApi.createHttpConnection(sourceUri);
+                        if (useHttps && trustEveryone) {
+                            // Setup the HTTPS connection class to trust everyone
+                            HttpsURLConnection https = (HttpsURLConnection)connection;
+                            oldSocketFactory = trustAllHosts(https);
+                            // Save the current hostnameVerifier
+                            oldHostnameVerifier = https.getHostnameVerifier();
+                            // Setup the connection not to verify hostnames
+                            https.setHostnameVerifier(DO_NOT_VERIFY);
+                        }
+        
+                        connection.setRequestMethod("GET");
+        
+                        // TODO: Make OkHttp use this AmazonCookieManager by default.
+                        String cookie = AmazonCookieManager.getInstance().getCookie(sourceUri.toString());
+                        if(cookie != null)
+                        {
+                            connection.setRequestProperty("cookie", cookie);
+                        }
+                        
+                        // This must be explicitly set for gzip progress tracking to work.
+                        connection.setRequestProperty("Accept-Encoding", "gzip");
+    
+                        // Handle the other headers
+                        if (headers != null) {
+                            addHeadersToRequest(connection, headers);
+                        }
+        
+                        connection.connect();
+    
+                        if (connection.getContentEncoding() == null || connection.getContentEncoding().equalsIgnoreCase("gzip"))
{
+                            // Only trust content-length header if we understand
+                            // the encoding -- identity or gzip
+                            progress.setLengthComputable(true);
+                            progress.setTotal(connection.getContentLength());
+                        }
+                        inputStream = getInputStream(connection);
+                    }
+                    
+                    try {
+                        synchronized (context) {
+                            if (context.aborted) {
+                                return;
+                            }
+                            context.currentInputStream = inputStream;
+                        }
+                        
+                        // write bytes to file
+                        byte[] buffer = new byte[MAX_BUFFER_SIZE];
+                        int bytesRead = 0;
+                        while ((bytesRead = inputStream.read(buffer)) > 0) {
+                            outputStream.write(buffer, 0, bytesRead);
+                            // Send a progress event.
+                            progress.setLoaded(inputStream.getTotalRawBytesRead());
+                            PluginResult progressResult = new PluginResult(PluginResult.Status.OK,
progress.toJSONObject());
+                            progressResult.setKeepCallback(true);
+                            context.sendPluginResult(progressResult);
+                        }
+                    } finally {
+                        context.currentInputStream = null;
+                        safeClose(inputStream);
+                        safeClose(outputStream);
+                    }
+    
+                    Log.d(LOG_TAG, "Saved file: " + target);
+    
+                    // create FileEntry object
+                    JSONObject fileEntry = FileUtils.getEntry(file);
+                    
+                    result = new PluginResult(PluginResult.Status.OK, fileEntry);
+                } catch (FileNotFoundException e) {
+                    JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source,
target, connection);
+                    Log.e(LOG_TAG, error.toString(), e);
+                    result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
+                } catch (IOException e) {
+                    JSONObject error = createFileTransferError(CONNECTION_ERR, source, target,
connection);
+                    Log.e(LOG_TAG, error.toString(), e);
+                    result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
+                } catch (JSONException e) {
+                    Log.e(LOG_TAG, e.getMessage(), e);
+                    result = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
+                } catch (Throwable e) {
+                    JSONObject error = createFileTransferError(CONNECTION_ERR, source, target,
connection);
+                    Log.e(LOG_TAG, error.toString(), e);
+                    result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
+                } finally {
+                    safeClose(outputStream);
+                    synchronized (activeRequests) {
+                        activeRequests.remove(objectId);
+                    }
+
+                    if (connection != null) {
+                        // Revert back to the proper verifier and socket factories
+                        if (trustEveryone && useHttps) {
+                            HttpsURLConnection https = (HttpsURLConnection) connection;
+                            https.setHostnameVerifier(oldHostnameVerifier);
+                            https.setSSLSocketFactory(oldSocketFactory);
+                        }
+                    }
+
+                    if (result == null) {
+                        result = new PluginResult(PluginResult.Status.ERROR, createFileTransferError(CONNECTION_ERR,
source, target, connection));
+                    }
+                    // Remove incomplete download.
+                    if (result.getStatus() != PluginResult.Status.OK.ordinal() &&
file != null) {
+                        file.delete();
+                    }
+                    context.sendPluginResult(result);
+                }
+            }
+        });
+    }
+
+    /**
+     * Abort an ongoing upload or download.
+     */
+    private void abort(String objectId) {
+        final RequestContext context;
+        synchronized (activeRequests) {
+            context = activeRequests.remove(objectId);
+        }
+        if (context != null) {
+            File file = context.targetFile;
+            if (file != null) {
+                file.delete();
+            }
+            // Trigger the abort callback immediately to minimize latency between it and
abort() being called.
+            JSONObject error = createFileTransferError(ABORTED_ERR, context.source, context.target,
null, -1);
+            synchronized (context) {
+                context.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, error));
+                context.aborted = true;
+            }
+            // Closing the streams can block, so execute on a background thread.
+            cordova.getThreadPool().execute(new Runnable() {
+                public void run() {
+                    synchronized (context) {
+                        safeClose(context.currentInputStream);
+                        safeClose(context.currentOutputStream);
+                    }
+                }
+            });
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/blob/a80ae041/test/cordova-incl.js
----------------------------------------------------------------------
diff --git a/test/cordova-incl.js b/test/cordova-incl.js
index a82c590..bd612be 100644
--- a/test/cordova-incl.js
+++ b/test/cordova-incl.js
@@ -22,6 +22,7 @@
 var PLAT;
 (function getPlatform() {
     var platforms = {
+        amazon_fireos: /cordova-amazon-fireos/,
         android: /Android/,
         ios: /(iPad)|(iPhone)|(iPod)/,
         blackberry10: /(BB10)/,
@@ -76,7 +77,7 @@ if (!window._doNotWriteCordovaScript) {
 }
 
 function backHome() {
-    if (window.device && device.platform && device.platform.toLowerCase()
== 'android') {
+    if (window.device && device.platform && (device.platform.toLowerCase()
== 'android' || device.platform.toLowerCase() == 'amazon-fireos')) {
         navigator.app.backHistory();
     }
     else {


Mime
View raw message