/*
 * Copyright 2018 New Vector Ltd
 *
 * Licensed 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.matrix.androidsdk.ssl;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.List;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

import okhttp3.TlsVersion;

/**
 * Force the usage of Tls versions on every created socket
 * Inspired from https://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/
 */
/*package*/ class TLSSocketFactory extends SSLSocketFactory {

    private SSLSocketFactory internalSSLSocketFactory;

    private String[] enabledProtocol;

    /**
     * Constructor
     *
     * @param trustPinned
     * @param acceptedTlsVersions
     * @throws KeyManagementException
     * @throws NoSuchAlgorithmException
     */
    /*package*/ TLSSocketFactory(TrustManager[] trustPinned, List<TlsVersion> acceptedTlsVersions) throws KeyManagementException, NoSuchAlgorithmException {
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, trustPinned, new SecureRandom());
        internalSSLSocketFactory = context.getSocketFactory();

        enabledProtocol = new String[acceptedTlsVersions.size()];
        int i = 0;
        for (TlsVersion tlsVersion : acceptedTlsVersions) {
            enabledProtocol[i] = tlsVersion.javaName();
            i++;
        }
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return internalSSLSocketFactory.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return internalSSLSocketFactory.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket() throws IOException {
        return enableTLSOnSocket(internalSSLSocketFactory.createSocket());
    }

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
    }

    private Socket enableTLSOnSocket(Socket socket) {
        if (socket != null && (socket instanceof SSLSocket)) {
            ((SSLSocket) socket).setEnabledProtocols(enabledProtocol);
        }
        return socket;
    }
}

@@ -628,16 +628,56 @@
 k.ssl;%0A%0A
+import org.matrix.androidsdk.util.Log;%0A%0A
 import j
@@ -914,16 +914,69 @@
 Random;%0A
+import java.util.ArrayList;%0Aimport java.util.Arrays;%0A
 import j
@@ -1381,16 +1381,98 @@
 actory %7B
+%0A    private static final String LOG_TAG = TLSSocketFactory.class.getSimpleName();
 %0A%0A    pr
@@ -4006,66 +4006,759 @@
     
-((SSLSocket) socket).setEnabledProtocols(enabledProtocol);
+SSLSocket sslSocket = (SSLSocket) socket;%0A%0A            List%3CString%3E supportedProtocols = Arrays.asList(sslSocket.getSupportedProtocols());%0A            List%3CString%3E filteredEnabledProtocols = new ArrayList%3C%3E();%0A%0A            for (String protocol : enabledProtocol) %7B%0A                if (supportedProtocols.contains(protocol)) %7B%0A                    filteredEnabledProtocols.add(protocol);%0A                %7D%0A            %7D%0A%0A            if (!filteredEnabledProtocols.isEmpty()) %7B%0A                try %7B%0A                    sslSocket.setEnabledProtocols(filteredEnabledProtocols.toArray(new String%5BfilteredEnabledProtocols.size()%5D));%0A                %7D catch (Exception e) %7B%0A                    Log.e(LOG_TAG, %22Exception: %22, e);%0A                %7D%0A            %7D
 %0A   

Fix crash on Android <= 19: do not enable TLS1.3