org.kontalk.upload.KontalkBoxUploadConnection.java Source code

Java tutorial

Introduction

Here is the source code for org.kontalk.upload.KontalkBoxUploadConnection.java

Source

/*
 * Kontalk Android client
 * Copyright (C) 2015 Kontalk Devteam <devteam@kontalk.org>
    
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
    
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
    
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.kontalk.upload;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HttpsURLConnection;

import org.apache.http.conn.ssl.AllowAllHostnameVerifier;

import android.content.Context;
import android.net.Uri;

import info.guardianproject.netcipher.NetCipher;

import org.kontalk.Kontalk;
import org.kontalk.client.ClientHTTPConnection;
import org.kontalk.client.EndpointServer;
import org.kontalk.crypto.Coder;
import org.kontalk.crypto.PersonalKey;
import org.kontalk.provider.UsersProvider;
import org.kontalk.service.ProgressListener;
import org.kontalk.util.Preferences;
import org.kontalk.util.ProgressInputStreamEntity;

/**
 * Upload service implementation for Kontalk Box dropbox service.
 * @author Daniele Ricci
 */
public class KontalkBoxUploadConnection implements UploadConnection {

    /** Message flags header. */
    private static final String HEADER_MESSAGE_FLAGS = "X-Message-Flags";

    protected final Context mContext;

    protected HttpsURLConnection currentRequest;

    private final static int CONNECT_TIMEOUT = 15000;
    private final static int READ_TIMEOUT = 40000;

    private final PrivateKey mPrivateKey;
    private final X509Certificate mCertificate;

    private final String mBaseUrl;

    public KontalkBoxUploadConnection(Context context, String url, PrivateKey privateKey,
            X509Certificate bridgeCert) {
        mContext = context;
        mBaseUrl = url;
        mPrivateKey = privateKey;
        mCertificate = bridgeCert;
    }

    @Override
    public void abort() {
        if (currentRequest != null)
            currentRequest.disconnect();
    }

    @Override
    public String upload(Uri uri, String mime, boolean encrypt, String to, ProgressListener listener)
            throws IOException {

        InputStream inMessage = null;
        try {
            inMessage = mContext.getContentResolver().openInputStream(uri);

            boolean encrypted = false;
            // check if we have to encrypt the message
            if (encrypt) {
                PersonalKey key = Kontalk.get(mContext).getPersonalKey();
                EndpointServer server = Preferences.getEndpointServer(mContext);
                Coder coder = UsersProvider.getEncryptCoder(mContext, server, key, new String[] { to });
                if (coder != null) {
                    // create a temporary file to store encrypted data
                    File temp = File.createTempFile("media", null, mContext.getCacheDir());
                    FileOutputStream out = new FileOutputStream(temp);

                    coder.encryptFile(inMessage, out);
                    // close original file and encrypted file
                    inMessage.close();
                    out.close();

                    // open the encrypted file
                    inMessage = new FileInputStream(temp);
                    encrypted = true;

                    // delete the encrypted file
                    // it will stay until all streams are closed
                    temp.delete();
                }
            }

            // http request!
            boolean acceptAnyCertificate = Preferences.getAcceptAnyCertificate(mContext);
            currentRequest = prepareMessage(mime, encrypted, acceptAnyCertificate);

            // execute!
            ProgressInputStreamEntity entity = new ProgressInputStreamEntity(inMessage, this, listener);
            entity.writeTo(currentRequest.getOutputStream());

            if (currentRequest.getResponseCode() != 200)
                throw new IOException(currentRequest.getResponseCode() + " " + currentRequest.getResponseMessage());

            return responseToString(currentRequest, Charset.defaultCharset());
        } catch (Exception e) {
            throw innerException("upload error", e);
        } finally {
            currentRequest = null;
            if (inMessage != null) {
                try {
                    inMessage.close();
                } catch (Exception e) {
                    // ignore
                }
            }
        }
    }

    public static String responseToString(HttpURLConnection conn, final Charset charset) throws IOException {
        final InputStream instream = conn.getInputStream();
        if (instream == null) {
            return null;
        }
        try {
            int i = conn.getContentLength();
            if (i < 0) {
                i = 4096;
            }
            final Reader reader = new InputStreamReader(instream, charset);
            final StringBuilder buffer = new StringBuilder(i);
            final char[] tmp = new char[1024];
            int l;
            while ((l = reader.read(tmp)) != -1) {
                buffer.append(tmp, 0, l);
            }
            return buffer.toString();
        } finally {
            instream.close();
        }
    }

    private IOException innerException(String detail, Throwable cause) {
        IOException ie = new IOException(detail);
        ie.initCause(cause);
        return ie;
    }

    private void setupClient(HttpsURLConnection conn, String mime, boolean encrypted, boolean acceptAnyCertificate)
            throws CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException,
            KeyManagementException, NoSuchProviderException, IOException {

        conn.setSSLSocketFactory(ClientHTTPConnection.setupSSLSocketFactory(mContext, mPrivateKey, mCertificate,
                acceptAnyCertificate));
        if (acceptAnyCertificate)
            conn.setHostnameVerifier(new AllowAllHostnameVerifier());
        conn.setRequestProperty("Content-Type", mime != null ? mime : "application/octet-stream");
        if (encrypted)
            conn.setRequestProperty(HEADER_MESSAGE_FLAGS, "encrypted");
        // bug caused by Lighttpd
        conn.setRequestProperty("Expect", "100-continue");

        conn.setConnectTimeout(CONNECT_TIMEOUT);
        conn.setReadTimeout(READ_TIMEOUT);
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setRequestMethod("POST");
    }

    /** A message posting method. */
    private HttpsURLConnection prepareMessage(String mime, boolean encrypted, boolean acceptAnyCertificate)
            throws IOException {

        // create uri
        HttpsURLConnection conn = NetCipher.getHttpsURLConnection(new URL(mBaseUrl));
        try {
            setupClient(conn, mime, encrypted, acceptAnyCertificate);
        } catch (Exception e) {
            throw new IOException("error setting up SSL connection", e);
        }

        return conn;
    }

}