liveplugin.toolwindow.addplugin.git.jetbrains.plugins.github.util.GithubSslSupport.java Source code

Java tutorial

Introduction

Here is the source code for liveplugin.toolwindow.addplugin.git.jetbrains.plugins.github.util.GithubSslSupport.java

Source

/*
 * Copyright 2000-2013 JetBrains s.r.o.
 *
 * 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 liveplugin.toolwindow.addplugin.git.jetbrains.plugins.github.util;

import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vcs.CalledInAwt;
import com.intellij.util.ThrowableConvertor;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import sun.security.validator.ValidatorException;

import java.io.IOException;

/**
 * Provides various methods to work with SSL certificate protected HTTPS connections.
 *
 * @author Kirill Likhodedov
 */
public class GithubSslSupport {
    private static GithubSslSupport instance;

    public static GithubSslSupport getInstance() {
        if (instance == null) {
            instance = new GithubSslSupport();
        }
        return instance;
    }

    /**
     * Tries to execute the {@link org.apache.commons.httpclient.HttpMethod} and captures the {@link sun.security.validator.ValidatorException exception} which is thrown if user connects
     * to an HTTPS server with a non-trusted (probably, self-signed) SSL certificate. In which case proposes to cancel the connection
     * or to proceed without certificate check.
     *
     * @param methodCreator a function to create the HttpMethod. This is required instead of just {@link org.apache.commons.httpclient.HttpMethod} instance, because the
     *                      implementation requires the HttpMethod to be recreated in certain circumstances.
     * @return the HttpMethod instance which was actually executed
     * and which can be {@link org.apache.commons.httpclient.HttpMethod#getResponseBodyAsString() asked for the response}.
     * @throws java.io.IOException in case of other errors or if user declines the proposal of non-trusted connection.
     */
    @NotNull
    public HttpMethod executeSelfSignedCertificateAwareRequest(@NotNull HttpClient client, @NotNull String uri,
            @NotNull ThrowableConvertor<String, HttpMethod, IOException> methodCreator) throws IOException {
        HttpMethod method = methodCreator.convert(uri);
        try {
            client.executeMethod(method);
            return method;
        } catch (IOException e) {
            HttpMethod m = handleCertificateExceptionAndRetry(e, method.getURI().getHost(), client, method.getURI(),
                    methodCreator);
            if (m == null) {
                throw e;
            }
            return m;
        }
    }

    @Nullable
    private static HttpMethod handleCertificateExceptionAndRetry(@NotNull IOException e, @NotNull String host,
            @NotNull HttpClient client, @NotNull URI uri,
            @NotNull ThrowableConvertor<String, HttpMethod, IOException> methodCreator) throws IOException {
        if (!isCertificateException(e)) {
            throw e;
        }

        if (isTrusted(host)) {
            // creating a special configuration that allows connections to non-trusted HTTPS hosts
            // see the javadoc to EasySSLProtocolSocketFactory for details
            Protocol easyHttps = new Protocol("https", (ProtocolSocketFactory) new EasySSLProtocolSocketFactory(),
                    443);
            HostConfiguration hc = new HostConfiguration();
            hc.setHost(host, 443, easyHttps);
            String relativeUri = new URI(uri.getPathQuery(), false).getURI();
            // it is important to use relative URI here, otherwise our custom protocol won't work.
            // we have to recreate the method, because HttpMethod#setUri won't overwrite the host,
            // and changing host by hands (HttpMethodBase#setHostConfiguration) is deprecated.
            HttpMethod method = methodCreator.convert(relativeUri);
            client.executeMethod(hc, method);
            return method;
        }
        throw e;
    }

    public static boolean isCertificateException(IOException e) {
        return e.getCause() instanceof ValidatorException;
    }

    private static boolean isTrusted(@NotNull String host) {
        return GithubSettings.getInstance().getTrustedHosts().contains(host.toLowerCase());
    }

    private static void saveToTrusted(@NotNull String host) {
        GithubSettings.getInstance().addTrustedHost(host.toLowerCase());
    }

    @CalledInAwt
    public boolean askIfShouldProceed(final String url) {
        String host = GithubUrlUtil.getHostFromUrl(url);

        int choice = Messages.showYesNoDialog(
                "The security certificate of " + host + " is not trusted. Do you want to proceed anyway?",
                "Not Trusted Certificate", "Proceed anyway", "No, I don't trust", Messages.getErrorIcon());
        boolean trust = (choice == Messages.YES);
        if (trust) {
            saveToTrusted(host);
        }
        return trust;
    }

}