<commit_before>/*
       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;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
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.net.URLDecoder;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Iterator;

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.api.Plugin;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.net.Uri;
import android.util.Log;
import android.webkit.CookieManager;

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 BOUNDARY =  "*****";

    public static int FILE_NOT_FOUND_ERR = 1;
    public static int INVALID_URL_ERR = 2;
    public static int CONNECTION_ERR = 3;

    private SSLSocketFactory defaultSSLSocketFactory = null;
    private HostnameVerifier defaultHostnameVerifier = null;

    /* (non-Javadoc)
    * @see org.apache.cordova.api.Plugin#execute(java.lang.String, org.json.JSONArray, java.lang.String)
    */
    @Override
    public PluginResult execute(String action, JSONArray args, String callbackId) {
        String source = null;
        String target = null;
        try {
            source = URLDecoder.decode(args.getString(0));
            target = args.getString(1);
        } catch (JSONException e) {
            Log.d(LOG_TAG, "Missing source or target");
            return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing source or target");
        }

        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);
        }
    }

    /**
     * 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);
            }<commit_msg>Adds FileTransfer support for upload headers.<commit_after>/*
       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;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
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.net.URLDecoder;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Iterator;

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.api.Plugin;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.net.Uri;
import android.util.Log;
import android.webkit.CookieManager;

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 BOUNDARY =  "*****";

    public static int FILE_NOT_FOUND_ERR = 1;
    public static int INVALID_URL_ERR = 2;
    public static int CONNECTION_ERR = 3;

    private SSLSocketFactory defaultSSLSocketFactory = null;
    private HostnameVerifier defaultHostnameVerifier = null;

    /* (non-Javadoc)
    * @see org.apache.cordova.api.Plugin#execute(java.lang.String, org.json.JSONArray, java.lang.String)
    */
    @Override
    public PluginResult execute(String action, JSONArray args, String callbackId) {
        String source = null;
        String target = null;
        try {
            source = URLDecoder.decode(args.getString(0));
            target = args.getString(1);
        } catch (JSONException e) {
            Log.d(LOG_TAG, "Missing source or target");
            return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing source or target");
        }

        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);
        }
    }

    /**
     * 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
            JSONObject headers = args.optJSONObject(8);
        	// Look for headers on the params map for backwards compatibility with older Cordova versions.
            if (headers == null && params != null) {
                headers = params.optJSONObject("headers");
            }

            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);

            // 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);
            
            // Set the cookies on the response
            String cookie = CookieManager.getInstance().getCookie(target);
            if (cookie != null) {
                conn.setRequestProperty("Cookie", cookie);
            }

            // Handle the other headers
            if (headers != null) {
                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));
                        }
                        conn.setRequestProperty(headerKey, headerValues.getString(0));
                        for (int i = 1; i < headerValues.length(); ++i) {
                            conn.addRequestProperty(headerKey, headerValues.getString(i));
                        }
                    }
                } catch (JSONException e1) {
                  // No headers to be manipulated!
                }
            }

            /*
                * 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);
            }
{'fraction_non_alphanumeric': 0.08956903945111493, 'fraction_numerical': 0.0034841337907375643, 'mean_word_length': 1.9557984790874525, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2396590', 'n_tokens_mistral': 4746, 'n_tokens_neox': 4539, 'n_words': 1441}