package com.buddycloud.http;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;

import org.apache.http.conn.scheme.LayeredSocketFactory;
import org.apache.http.params.HttpParams;

import android.annotation.TargetApi;
import android.net.SSLCertificateSocketFactory;
import android.os.Build;

import com.buddycloud.log.Logger;

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public class TLSSNISocketFactory implements LayeredSocketFactory {
    private static final String TAG = "buddycloud.SNISocketFactory";

    // "insecure" means that it doesn't verify the host name
    // we will do this ourselves so we can set up SNI before
    SSLCertificateSocketFactory sslSocketFactory =
            (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getInsecure(0, null);


    // Plain TCP/IP (layer below TLS)

    @Override
    public Socket connectSocket(Socket s, String host, int port, InetAddress localAddress, int localPort, HttpParams params) throws IOException {
        s.connect(new InetSocketAddress(host, port));
        return s;
    }

    @Override
    public Socket createSocket() {
        Socket s = new Socket();
        return s;
    }

    @Override
    public boolean isSecure(Socket s) throws IllegalArgumentException {
        if (s instanceof SSLSocket)
            return ((SSLSocket)s).isConnected();
        return false;
    }


    // TLS layer

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        SSLSocket ssl = (SSLSocket)sslSocketFactory.createSocket(s, host, port, autoClose);

        // set SNI before the handshake
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            Logger.info(TAG, "Setting SNI hostname");
            sslSocketFactory.setHostname(ssl, host);
        } else {
        	Logger.warn(TAG, "No SNI support below Android 4.2!");
        }

        // now do the TLS handshake
        ssl.startHandshake();
        SSLSession session = ssl.getSession();
        if (session == null)
        throw new SSLException("Cannot verify SSL socket without session");

        // verify host name (important!)
        if (!HttpsURLConnection.getDefaultHostnameVerifier().verify(host, session))
        throw new SSLPeerUnverifiedException("Cannot verify hostname: " + host);
        return ssl;
    }
}