net.java.sip.communicator.service.httputil.HttpUtils.java Source code

Java tutorial

Introduction

Here is the source code for net.java.sip.communicator.service.httputil.HttpUtils.java

Source

/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package net.java.sip.communicator.service.httputil;

import java.io.*;
import java.net.*;
import java.security.*;
import java.util.*;

import javax.net.ssl.*;

import net.java.sip.communicator.util.Logger;
import net.java.sip.communicator.service.gui.*;

import org.apache.http.*;
import org.apache.http.Header;
import org.apache.http.ProtocolException;
import org.apache.http.auth.*;
import org.apache.http.client.*;
import org.apache.http.client.methods.*;
import org.apache.http.client.params.*;
import org.apache.http.client.utils.*;
import org.apache.http.conn.scheme.*;
import org.apache.http.entity.*;
import org.apache.http.entity.mime.*;
import org.apache.http.entity.mime.content.*;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.*;
import org.apache.http.message.*;
import org.apache.http.params.*;
import org.apache.http.protocol.*;
import org.apache.http.util.*;
import org.jitsi.util.*;

/**
 * Common http utils querying http locations, handling redirects, self-signed
 * certificates, host verify on certificates, password protection and storing
 * and reusing credentials for password protected sites.
 *
 * @author Damian Minkov
 */
public class HttpUtils {
    /**
     * The <tt>Logger</tt> used by the <tt>HttpUtils</tt> class for logging
     * output.
     */
    private static final Logger logger = Logger.getLogger(HttpUtils.class);

    /**
     * The prefix used when storing credentials for sites when no property
     * is provided.
     */
    private static final String HTTP_CREDENTIALS_PREFIX = "net.java.sip.communicator.util.http.credential.";

    /**
     * Maximum number of http redirects (301, 302, 303).
     */
    private static final int MAX_REDIRECTS = 10;

    /**
     * Opens a connection to the <tt>address</tt>.
     * @param address the address to contact.
     * @return the result if any or null if connection was not possible
     * or canceled by user.
     */
    public static HTTPResponseResult openURLConnection(String address) {
        return openURLConnection(address, null, null, null, null);
    }

    /**
     * Opens a connection to the <tt>address</tt>.
     * @param address the address to contact.
     * @param headerParamNames additional header name to include
     * @param headerParamValues corresponding header value to include
     * @return the result if any or null if connection was not possible
     * or canceled by user.
     */
    public static HTTPResponseResult openURLConnection(String address, String[] headerParamNames,
            String[] headerParamValues) {
        return openURLConnection(address, null, null, headerParamNames, headerParamValues);
    }

    /**
     * Opens a connection to the <tt>address</tt>.
     * @param address the address to contact.
     * @param usernamePropertyName the property to use to retrieve/store
     * username value if protected site is hit, for username
     * ConfigurationService service is used.
     * @param passwordPropertyName the property to use to retrieve/store
     * password value if protected site is hit, for password
     * CredentialsStorageService service is used.
     * @param headerParamNames additional header name to include
     * @param headerParamValues corresponding header value to include
     * @return the result if any or null if connection was not possible
     * or canceled by user.
     */
    public static HTTPResponseResult openURLConnection(String address, String usernamePropertyName,
            String passwordPropertyName, String[] headerParamNames, String[] headerParamValues) {
        try {
            HttpGet httpGet = new HttpGet(address);
            DefaultHttpClient httpClient = getHttpClient(usernamePropertyName, passwordPropertyName,
                    httpGet.getURI().getHost(), null);

            /* add additional HTTP header */
            if (headerParamNames != null && headerParamValues != null) {
                for (int i = 0; i < headerParamNames.length; i++) {
                    httpGet.addHeader(new BasicHeader(headerParamNames[i], headerParamValues[i]));
                }
            }

            HttpEntity result = executeMethod(httpClient, httpGet, null, null);

            if (result == null)
                return null;

            return new HTTPResponseResult(result, httpClient);
        } catch (Throwable t) {
            logger.error("Cannot open connection to:" + address, t);
        }

        return null;
    }

    /**
     * Executes the method and return the result. Handle ask for password
     * when hitting password protected site.
     * Keep asking for password till user clicks cancel or enters correct
     * password. When 'remember password' is checked password is saved, if this
     * password and username are not correct clear them, if there are correct
     * they stay saved.
     * @param httpClient the configured http client to use.
     * @param req the request for now it is get or post.
     * @param redirectHandler handles redirection, should we redirect and
     * the actual redirect.
     * @param parameters if we are redirecting we can use already filled
     * username and password in order to avoid asking the user twice.
     *
     * @return the result http entity.
     */
    private static HttpEntity executeMethod(DefaultHttpClient httpClient, HttpRequestBase req,
            RedirectHandler redirectHandler, List<NameValuePair> parameters) throws Throwable {
        // do it when response (first execution) or till we are unauthorized
        HttpResponse response = null;
        int redirects = 0;
        while (response == null || response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED
                || response.getStatusLine().getStatusCode() == HttpStatus.SC_FORBIDDEN) {
            // if we were unauthorized, lets clear the method and recreate it
            // for new connection with new credentials.
            if (response != null && (response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED
                    || response.getStatusLine().getStatusCode() == HttpStatus.SC_FORBIDDEN)) {
                if (logger.isDebugEnabled())
                    logger.debug("Will retry http connect and " + "credentials input as latest are not correct!");

                throw new AuthenticationException("Authorization needed");
            } else
                response = httpClient.execute(req);

            // if user click cancel no need to retry, stop trying
            if (!((HTTPCredentialsProvider) httpClient.getCredentialsProvider()).retry()) {
                if (logger.isDebugEnabled())
                    logger.debug("User canceled credentials input.");
                break;
            }

            // check for post redirect as post redirects are not handled
            // automatically
            // RFC2616 (10.3 Redirection 3xx).
            // The second request (forwarded method) can only be a GET or HEAD.
            Header locationHeader = response.getFirstHeader("location");

            if (locationHeader != null && req instanceof HttpPost
                    && (response.getStatusLine().getStatusCode() == HttpStatus.SC_MOVED_PERMANENTLY
                            || response.getStatusLine().getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY
                            || response.getStatusLine().getStatusCode() == HttpStatus.SC_SEE_OTHER)
                    && redirects < MAX_REDIRECTS) {
                HttpRequestBase oldreq = req;
                oldreq.abort();

                String newLocation = locationHeader.getValue();

                // lets ask redirection handler if any
                if (redirectHandler != null && redirectHandler.handleRedirect(newLocation, parameters)) {
                    return null;
                }

                req = new HttpGet(newLocation);
                req.setParams(oldreq.getParams());
                req.setHeaders(oldreq.getAllHeaders());

                redirects++;
                response = httpClient.execute(req);
            }
        }

        // if we finally managed to login return the result.
        if (response != null && response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            return response.getEntity();
        }

        // is user has canceled no result needed.
        return null;
    }

    /**
     * Posts a <tt>file</tt> to the <tt>address</tt>.
     * @param address the address to post the form to.
     * @param fileParamName the name of the param for the file.
     * @param file the file we will send.
     * @return the result or null if send was not possible or
     * credentials ask if any was canceled.
     */
    public static HTTPResponseResult postFile(String address, String fileParamName, File file) {
        return postFile(address, fileParamName, file, null, null);
    }

    /**
     * Posts a <tt>file</tt> to the <tt>address</tt>.
     * @param address the address to post the form to.
     * @param fileParamName the name of the param for the file.
     * @param file the file we will send.
     * @param usernamePropertyName the property to use to retrieve/store
     * username value if protected site is hit, for username
     * ConfigurationService service is used.
     * @param passwordPropertyName the property to use to retrieve/store
     * password value if protected site is hit, for password
     * CredentialsStorageService service is used.
     * @return the result or null if send was not possible or
     * credentials ask if any was canceled.
     */
    public static HTTPResponseResult postFile(String address, String fileParamName, File file,
            String usernamePropertyName, String passwordPropertyName) {
        DefaultHttpClient httpClient = null;
        try {
            HttpPost postMethod = new HttpPost(address);

            httpClient = getHttpClient(usernamePropertyName, passwordPropertyName, postMethod.getURI().getHost(),
                    null);

            String mimeType = URLConnection.guessContentTypeFromName(file.getPath());
            if (mimeType == null)
                mimeType = "application/octet-stream";

            FileBody bin = new FileBody(file, mimeType);

            MultipartEntity reqEntity = new MultipartEntity();
            reqEntity.addPart(fileParamName, bin);

            postMethod.setEntity(reqEntity);

            HttpEntity resEntity = executeMethod(httpClient, postMethod, null, null);

            if (resEntity == null)
                return null;

            return new HTTPResponseResult(resEntity, httpClient);
        } catch (Throwable e) {
            logger.error("Cannot post file to:" + address, e);
        }

        return null;
    }

    /**
     * Posting form to <tt>address</tt>. For submission we use POST method
     * which is "application/x-www-form-urlencoded" encoded.
     * @param address HTTP address.
     * @param usernamePropertyName the property to use to retrieve/store
     * username value if protected site is hit, for username
     * ConfigurationService service is used.
     * @param passwordPropertyName the property to use to retrieve/store
     * password value if protected site is hit, for password
     * CredentialsStorageService service is used.
     * @param formParamNames the parameter names to include in post.
     * @param formParamValues the corresponding parameter values to use.
     * @param usernameParamIx the index of the username parameter in the
     * <tt>formParamNames</tt> and <tt>formParamValues</tt>
     * if any, otherwise -1.
     * @param passwordParamIx the index of the password parameter in the
     * <tt>formParamNames</tt> and <tt>formParamValues</tt>
     * if any, otherwise -1.
     * @return the result or null if send was not possible or
     * credentials ask if any was canceled.
     */
    public static HTTPResponseResult postForm(String address, String usernamePropertyName,
            String passwordPropertyName, ArrayList<String> formParamNames, ArrayList<String> formParamValues,
            int usernameParamIx, int passwordParamIx) throws Throwable {
        return postForm(address, usernamePropertyName, passwordPropertyName, formParamNames, formParamValues,
                usernameParamIx, passwordParamIx, null);
    }

    /**
     * Posting form to <tt>address</tt>. For submission we use POST method
     * which is "application/x-www-form-urlencoded" encoded.
     * @param address HTTP address.
     * @param usernamePropertyName the property to use to retrieve/store
     * username value if protected site is hit, for username
     * ConfigurationService service is used.
     * @param passwordPropertyName the property to use to retrieve/store
     * password value if protected site is hit, for password
     * CredentialsStorageService service is used.
     * @param formParamNames the parameter names to include in post.
     * @param formParamValues the corresponding parameter values to use.
     * @param usernameParamIx the index of the username parameter in the
     * <tt>formParamNames</tt> and <tt>formParamValues</tt>
     * if any, otherwise -1.
     * @param passwordParamIx the index of the password parameter in the
     * <tt>formParamNames</tt> and <tt>formParamValues</tt>
     * if any, otherwise -1.
     * @param redirectHandler handles redirection, should we redirect and
     * the actual redirect.
     * @return the result or null if send was not possible or
     * credentials ask if any was canceled.
     */
    public static HTTPResponseResult postForm(String address, String usernamePropertyName,
            String passwordPropertyName, ArrayList<String> formParamNames, ArrayList<String> formParamValues,
            int usernameParamIx, int passwordParamIx, RedirectHandler redirectHandler) throws Throwable {
        return postForm(address, usernamePropertyName, passwordPropertyName, formParamNames, formParamValues,
                usernameParamIx, passwordParamIx, redirectHandler, null, null);
    }

    /**
     * Posting form to <tt>address</tt>. For submission we use POST method
     * which is "application/x-www-form-urlencoded" encoded.
     * @param address HTTP address.
     * @param headerParamNames additional header name to include
     * @param headerParamValues corresponding header value to include
     * @return the result or null if send was not possible or
     * credentials ask if any was canceled.
     */
    public static HTTPResponseResult postForm(String address, List<String> headerParamNames,
            List<String> headerParamValues) throws Throwable {
        return postForm(address, null, null, null, null, -1, -1, null, headerParamNames, headerParamValues);
    }

    /**
     * Posting form to <tt>address</tt>. For submission we use POST method
     * which is "application/x-www-form-urlencoded" encoded.
     * @param address HTTP address.
     * @param usernamePropertyName the property to use to retrieve/store
     * username value if protected site is hit, for username
     * ConfigurationService service is used.
     * @param passwordPropertyName the property to use to retrieve/store
     * password value if protected site is hit, for password
     * CredentialsStorageService service is used.
     * @param formParamNames the parameter names to include in post.
     * @param formParamValues the corresponding parameter values to use.
     * @param usernameParamIx the index of the username parameter in the
     * <tt>formParamNames</tt> and <tt>formParamValues</tt>
     * if any, otherwise -1.
     * @param passwordParamIx the index of the password parameter in the
     * <tt>formParamNames</tt> and <tt>formParamValues</tt>
     * if any, otherwise -1.
     * @param redirectHandler handles redirection, should we redirect and
     * the actual redirect.
     * @param headerParamNames additional header name to include
     * @param headerParamValues corresponding header value to include
     * @return the result or null if send was not possible or
     * credentials ask if any was canceled.
     */
    public static HTTPResponseResult postForm(String address, String usernamePropertyName,
            String passwordPropertyName, ArrayList<String> formParamNames, ArrayList<String> formParamValues,
            int usernameParamIx, int passwordParamIx, RedirectHandler redirectHandler,
            List<String> headerParamNames, List<String> headerParamValues) throws Throwable {
        DefaultHttpClient httpClient;
        HttpPost postMethod;
        HttpEntity resEntity = null;

        // if any authentication exception rise while executing
        // will retry
        AuthenticationException authEx;
        HTTPCredentialsProvider credentialsProvider = null;
        do {
            postMethod = new HttpPost(address);
            httpClient = getHttpClient(usernamePropertyName, passwordPropertyName, postMethod.getURI().getHost(),
                    credentialsProvider);

            try {
                // execute post
                resEntity = postForm(httpClient, postMethod, address, formParamNames, formParamValues,
                        usernameParamIx, passwordParamIx, redirectHandler, headerParamNames, headerParamValues);

                authEx = null;
            } catch (AuthenticationException ex) {
                authEx = ex;

                // lets reuse credentialsProvider
                credentialsProvider = (HTTPCredentialsProvider) httpClient.getCredentialsProvider();
                String userName = credentialsProvider.authUsername;

                // clear
                credentialsProvider.clear();

                // lets show the same username
                credentialsProvider.authUsername = userName;
                credentialsProvider.errorMessage = HttpUtilActivator.getResources().getI18NString(
                        "service.gui.AUTHENTICATION_FAILED",
                        new String[] { credentialsProvider.usedScope.getHost() });
            }
        } while (authEx != null);

        // canceled or no result
        if (resEntity == null)
            return null;

        return new HTTPResponseResult(resEntity, httpClient);
    }

    /**
     * Posting form to <tt>address</tt>. For submission we use POST method
     * which is "application/x-www-form-urlencoded" encoded.
     * @param httpClient the http client
     * @param postMethod the post method
     * @param address HTTP address.
     * @param formParamNames the parameter names to include in post.
     * @param formParamValues the corresponding parameter values to use.
     * @param usernameParamIx the index of the username parameter in the
     * <tt>formParamNames</tt> and <tt>formParamValues</tt>
     * if any, otherwise -1.
     * @param passwordParamIx the index of the password parameter in the
     * <tt>formParamNames</tt> and <tt>formParamValues</tt>
     * if any, otherwise -1.
     * @param headerParamNames additional header name to include
     * @param headerParamValues corresponding header value to include
     * @return the result or null if send was not possible or
     * credentials ask if any was canceled.
     */
    private static HttpEntity postForm(DefaultHttpClient httpClient, HttpPost postMethod, String address,
            ArrayList<String> formParamNames, ArrayList<String> formParamValues, int usernameParamIx,
            int passwordParamIx, RedirectHandler redirectHandler, List<String> headerParamNames,
            List<String> headerParamValues) throws Throwable {
        // if we have username and password in the parameters, lets
        // retrieve their values
        // if there are already filled skip asking the user
        Credentials creds = null;
        if (usernameParamIx != -1 && usernameParamIx < formParamNames.size() && passwordParamIx != -1
                && passwordParamIx < formParamNames.size()
                && (formParamValues.get(usernameParamIx) == null
                        || formParamValues.get(usernameParamIx).length() == 0)
                && (formParamValues.get(passwordParamIx) == null
                        || formParamValues.get(passwordParamIx).length() == 0)) {
            URL url = new URL(address);
            HTTPCredentialsProvider prov = (HTTPCredentialsProvider) httpClient.getCredentialsProvider();

            // don't allow empty username
            while (creds == null || creds.getUserPrincipal() == null
                    || StringUtils.isNullOrEmpty(creds.getUserPrincipal().getName())) {
                creds = prov.getCredentials(new AuthScope(url.getHost(), url.getPort()));

                // it was user canceled lets stop processing
                if (creds == null && !prov.retry()) {
                    return null;
                }
            }
        }

        // construct the name value pairs we will be sending
        List<NameValuePair> parameters = new ArrayList<NameValuePair>();
        // there can be no params
        if (formParamNames != null) {
            for (int i = 0; i < formParamNames.size(); i++) {
                // we are on the username index, insert retrieved username value
                if (i == usernameParamIx && creds != null) {
                    parameters
                            .add(new BasicNameValuePair(formParamNames.get(i), creds.getUserPrincipal().getName()));
                } // we are on the password index, insert retrieved password val
                else if (i == passwordParamIx && creds != null) {
                    parameters.add(new BasicNameValuePair(formParamNames.get(i), creds.getPassword()));
                } else // common name value pair, all info is present
                {
                    parameters.add(new BasicNameValuePair(formParamNames.get(i), formParamValues.get(i)));
                }
            }
        }

        // our custom strategy, will check redirect handler should we redirect
        // if missing will use the default handler
        httpClient.setRedirectStrategy(new CustomRedirectStrategy(redirectHandler, parameters));

        // Uses String UTF-8 to keep compatible with android version and
        // older versions of the http client libs, as the one used
        // in debian (4.1.x)
        String s = URLEncodedUtils.format(parameters, "UTF-8");
        StringEntity entity = new StringEntity(s, "UTF-8");
        // set content type to "application/x-www-form-urlencoded"
        entity.setContentType(URLEncodedUtils.CONTENT_TYPE);

        // insert post values encoded.
        postMethod.setEntity(entity);

        if (headerParamNames != null) {
            for (int i = 0; i < headerParamNames.size(); i++) {
                postMethod.addHeader(headerParamNames.get(i), headerParamValues.get(i));
            }
        }

        // execute post
        return executeMethod(httpClient, postMethod, redirectHandler, parameters);
    }

    /**
     * Returns the preconfigured http client,
     * using CertificateVerificationService, timeouts, user-agent,
     * hostname verifier, proxy settings are used from global java settings,
     * if protected site is hit asks for credentials
     * using util.swing.AuthenticationWindow.
     * @param usernamePropertyName the property to use to retrieve/store
     * username value if protected site is hit, for username
     * ConfigurationService service is used.
     * @param passwordPropertyName the property to use to retrieve/store
     * password value if protected site is hit, for password
     * CredentialsStorageService service is used.
     * @param credentialsProvider if not null provider will bre reused
     * in the new client
     * @param address the address we will be connecting to
     */
    public static DefaultHttpClient getHttpClient(String usernamePropertyName, String passwordPropertyName,
            final String address, CredentialsProvider credentialsProvider) throws IOException {
        HttpParams params = new BasicHttpParams();
        params.setParameter(CoreConnectionPNames.SO_TIMEOUT, 10000);
        params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000);
        params.setParameter(ClientPNames.MAX_REDIRECTS, MAX_REDIRECTS);

        DefaultHttpClient httpClient = new DefaultHttpClient(params);

        HttpProtocolParams.setUserAgent(httpClient.getParams(),
                System.getProperty("sip-communicator.application.name") + "/"
                        + System.getProperty("sip-communicator.version"));

        SSLContext sslCtx;
        try {
            sslCtx = HttpUtilActivator.getCertificateVerificationService()
                    .getSSLContext(HttpUtilActivator.getCertificateVerificationService().getTrustManager(address));
        } catch (GeneralSecurityException e) {
            throw new IOException(e.getMessage());
        }

        // note to any reviewer concerned about ALLOW_ALL_HOSTNAME_VERIFIER:
        // the SSL context obtained from the certificate service takes care of
        // certificate validation
        try {
            Scheme sch = new Scheme("https", 443, new SSLSocketFactoryEx(sslCtx));
            httpClient.getConnectionManager().getSchemeRegistry().register(sch);
        } catch (Throwable t) {
            logger.error("Error creating ssl socket factory", t);
        }

        // set proxy from default jre settings
        ProxySelectorRoutePlanner routePlanner = new ProxySelectorRoutePlanner(
                httpClient.getConnectionManager().getSchemeRegistry(), ProxySelector.getDefault());
        httpClient.setRoutePlanner(routePlanner);

        if (credentialsProvider == null)
            credentialsProvider = new HTTPCredentialsProvider(usernamePropertyName, passwordPropertyName);
        httpClient.setCredentialsProvider(credentialsProvider);

        // enable retry connecting with default retry handler
        // when connecting has prompted for authentication
        // connection can be disconnected nefore user answers and
        // we need to retry connection, using the credentials provided
        httpClient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(3, true));

        return httpClient;
    }

    /**
     * The provider asking for password that is inserted into httpclient.
     */
    private static class HTTPCredentialsProvider implements CredentialsProvider {
        /**
         * Should we continue retrying, this is set when user hits cancel.
         */
        private boolean retry = true;

        /**
         * The last scope we have used, no problem overriding cause
         * we use new HTTPCredentialsProvider instance for every
         * httpclient/request.
         */
        private AuthScope usedScope = null;

        /**
         * The property to use to retrieve/store
         * username value if protected site is hit, for username
         * ConfigurationService service is used.
         */
        private String usernamePropertyName = null;

        /**
         * The property to use to retrieve/store
         * password value if protected site is hit, for password
         * CredentialsStorageService service is used.
         */
        private String passwordPropertyName = null;

        /**
         * Authentication username if any.
         */
        private String authUsername = null;

        /**
         * Authentication password if any.
         */
        private String authPassword = null;

        /**
         * Error message.
         */
        private String errorMessage = null;

        /**
         * Creates HTTPCredentialsProvider.
         * @param usernamePropertyName the property to use to retrieve/store
         * username value if protected site is hit, for username
         * ConfigurationService service is used.
         * @param passwordPropertyName the property to use to retrieve/store
         * password value if protected site is hit, for password
         * CredentialsStorageService service is used.
         */
        HTTPCredentialsProvider(String usernamePropertyName, String passwordPropertyName) {
            this.usernamePropertyName = usernamePropertyName;
            this.passwordPropertyName = passwordPropertyName;
        }

        /**
         * Not used.
         */
        public void setCredentials(AuthScope authscope, Credentials credentials) {
        }

        /**
         * Get the {@link org.apache.http.auth.Credentials credentials} for the
         * given authentication scope.
         *
         * @param authscope the {@link org.apache.http.auth.AuthScope
         *                  authentication scope}
         * @return the credentials
         * @see #setCredentials(org.apache.http.auth.AuthScope,
         *      org.apache.http.auth.Credentials)
         */
        public Credentials getCredentials(AuthScope authscope) {
            this.usedScope = authscope;

            // if we have specified password and username property will use them
            // if not create one from the scope/site we are connecting to.
            if (passwordPropertyName == null)
                passwordPropertyName = getCredentialProperty(authscope);
            if (usernamePropertyName == null)
                usernamePropertyName = getCredentialProperty(authscope);

            // load the password
            String pass = HttpUtilActivator.getCredentialsService().loadPassword(passwordPropertyName);

            // if password is not saved ask user for credentials
            if (pass == null) {
                AuthenticationWindowService authenticationWindowService = HttpUtilActivator
                        .getAuthenticationWindowService();

                if (authenticationWindowService == null) {
                    logger.error("No AuthenticationWindowService implementation");
                    return null;
                }

                AuthenticationWindowService.AuthenticationWindow authWindow = authenticationWindowService.create(
                        authUsername, null, authscope.getHost(), true, false, null, null, null, null, null,
                        errorMessage,
                        HttpUtilActivator.getResources().getSettingsString("plugin.provisioning.SIGN_UP_LINK"));
                authWindow.setVisible(true);

                if (!authWindow.isCanceled()) {
                    Credentials cred = new UsernamePasswordCredentials(authWindow.getUserName(),
                            new String(authWindow.getPassword()));

                    authUsername = authWindow.getUserName();
                    authPassword = new String(authWindow.getPassword());

                    // if password remember is checked lets save passwords,
                    // if they seem not correct later will be removed.
                    if (authWindow.isRememberPassword()) {
                        HttpUtilActivator.getConfigurationService().setProperty(usernamePropertyName,
                                authWindow.getUserName());
                        HttpUtilActivator.getCredentialsService().storePassword(passwordPropertyName,
                                new String(authWindow.getPassword()));
                    }

                    return cred;
                }

                // well user canceled credentials input stop retry asking him
                // if credentials are not correct
                retry = false;
            } else {
                // we have saved values lets return them
                authUsername = HttpUtilActivator.getConfigurationService().getString(usernamePropertyName);
                authPassword = pass;

                return new UsernamePasswordCredentials(
                        HttpUtilActivator.getConfigurationService().getString(usernamePropertyName), pass);
            }

            return null;
        }

        /**
         * Clear saved password. Used when we are in situation that
         * saved username and password are no longer valid.
         */
        public void clear() {
            if (usedScope != null) {
                if (passwordPropertyName == null)
                    passwordPropertyName = getCredentialProperty(usedScope);
                if (usernamePropertyName == null)
                    usernamePropertyName = getCredentialProperty(usedScope);

                HttpUtilActivator.getConfigurationService().removeProperty(usernamePropertyName);
                HttpUtilActivator.getCredentialsService().removePassword(passwordPropertyName);
            }
            authUsername = null;
            authPassword = null;
            errorMessage = null;
        }

        /**
         * Constructs property name for save if one is not specified.
         * Its in the form
         * HTTP_CREDENTIALS_PREFIX.host.realm.port
         * @param authscope the scope, holds host,realm, port info about
         * the host we are reaching.
         * @return return the constructed property.
         */
        private static String getCredentialProperty(AuthScope authscope) {
            StringBuilder pref = new StringBuilder();

            pref.append(HTTP_CREDENTIALS_PREFIX).append(authscope.getHost()).append(".")
                    .append(authscope.getRealm()).append(".").append(authscope.getPort());

            return pref.toString();
        }

        /**
         * Whether we need to continue retrying.
         * @return whether we need to continue retrying.
         */
        boolean retry() {
            return retry;
        }

        /**
         * Returns authentication username if any
         * @return authentication username or null
         */
        public String getAuthenticationUsername() {
            return authUsername;
        }

        /**
         * Returns authentication password if any
         * @return authentication password or null
         */
        public String getAuthenticationPassword() {
            return authPassword;
        }
    }

    /**
     * Input stream wrapper which handles closing the httpclient when
     * everything is retrieved.
     */
    private static class HttpClientInputStream extends InputStream {
        /**
         * The original input stream.
         */
        InputStream in;

        /**
         * The http client to close.
         */
        HttpClient httpClient;

        /**
         * Creates HttpClientInputStream.
         * @param in the original input stream.
         * @param httpClient the http client to close.
         */
        HttpClientInputStream(InputStream in, HttpClient httpClient) {
            this.in = in;
            this.httpClient = httpClient;
        }

        /**
         * Uses parent InputStream read method.
         *
         * @return the next byte of data, or <code>-1</code> if the end of the
         *         stream is reached.
         * @throws java.io.IOException if an I/O error occurs.
         */
        @Override
        public int read() throws IOException {
            return in.read();
        }

        /**
         * Closes this input stream and releases any system resources associated
         * with the stream. Releases httpclient connections.
         *
         * <p> The <code>close</code> method of <code>InputStream</code> does
         * nothing.
         *
         * @exception  IOException  if an I/O error occurs.
         */
        @Override
        public void close() throws IOException {
            super.close();

            // When HttpClient instance is no longer needed,
            // shut down the connection manager to ensure
            // immediate de-allocation of all system resources
            httpClient.getConnectionManager().shutdown();
        }
    }

    /**
     * Utility class wraps the http requests result and some utility methods
     * for retrieving info and content for the result.
     */
    public static class HTTPResponseResult {
        /**
         * The httpclient entity.
         */
        HttpEntity entity;

        /**
         * The httpclient.
         */
        DefaultHttpClient httpClient;

        /**
         * Creates HTTPResponseResult.
         * @param entity the httpclient entity.
         * @param httpClient the httpclient.
         */
        HTTPResponseResult(HttpEntity entity, DefaultHttpClient httpClient) {
            this.entity = entity;
            this.httpClient = httpClient;
        }

        /**
         * Tells the length of the content, if known.
         *
         * @return  the number of bytes of the content, or
         *          a negative number if unknown. If the content length is known
         *          but exceeds {@link java.lang.Long#MAX_VALUE Long.MAX_VALUE},
         *          a negative number is returned.
         */
        public long getContentLength() {
            return entity.getContentLength();
        }

        /**
        * Returns a content stream of the entity.
        *
        * @return content stream of the entity.
        *
        * @throws IOException if the stream could not be created
        * @throws IllegalStateException
        *  if content stream cannot be created.
        */
        public InputStream getContent() throws IOException, IllegalStateException {
            return new HttpClientInputStream(entity.getContent(), httpClient);
        }

        /**
         * Returns a content string of the entity.
         *
         * @return content string of the entity.
         *
         * @throws IOException if the stream could not be created
         */
        public String getContentString() throws IOException {
            try {
                return EntityUtils.toString(entity);
            } finally {
                if (httpClient != null)
                    httpClient.getConnectionManager().shutdown();
            }
        }

        /**
         * Get the credentials used by the request.
         *
         * @return the credentials (login at index 0 and password at index 1)
         */
        public String[] getCredentials() {
            String cred[] = new String[2];

            if (httpClient != null) {
                HTTPCredentialsProvider prov = (HTTPCredentialsProvider) httpClient.getCredentialsProvider();
                cred[0] = prov.getAuthenticationUsername();
                cred[1] = prov.getAuthenticationPassword();
            }

            return cred;
        }
    }

    /**
     * Custom redirect handler that extends DefaultRedirectStrategy
     * We will check redirect handler should we redirect
     * If redirect handler is missing will continue with default strategy
     */
    private static class CustomRedirectStrategy extends DefaultRedirectStrategy {
        /**
         * The redirect handler to check.
         */
        private final RedirectHandler handler;

        /**
         * The already filled parameters to be used when redirecting.
         */
        private final List<NameValuePair> parameters;

        /**
         * Created custom redirect strategy.
         * @param handler the redirect handler.
         * @param parameters already filled parameters.
         */
        CustomRedirectStrategy(RedirectHandler handler, List<NameValuePair> parameters) {
            this.handler = handler;
            this.parameters = parameters;
        }

        /**
         * Check whether we need to redirect.
         * @param request the initial request
         * @param response the response containing the location param for
         * redirect.
         * @param context the http context.
         * @return should we redirect.
         * @throws ProtocolException
         */
        public boolean isRedirected(final HttpRequest request, final HttpResponse response,
                final HttpContext context) throws ProtocolException {
            Header locationHeader = response.getFirstHeader("location");

            if (handler != null && locationHeader != null && handler.hasParams(locationHeader.getValue())) {
                //we will cancel this redirect and will schedule new redirect
                handler.handleRedirect(locationHeader.getValue(), parameters);
                return false;
            }

            return super.isRedirected(request, response, context);
        }
    }

    /**
     * The redirect handler will cancel/proceed the redirection. Can
     * schedule new request with the redirect location, reusing the already
     * filled parameters.
     */
    public static interface RedirectHandler {
        /**
         * Schedule new request with the redirect location, reusing the already
         * filled parameters.
         *
         * @param location the new location.
         * @param parameters the parameters that were already filled.
         * @return should we continue with normal redirect.
         */
        public boolean handleRedirect(String location, List<NameValuePair> parameters);

        /**
         * Do the new location has params that need to be filled, return
         * <tt>true</tt> will cause to handle redirect.
         * @param location the new location.
         * @return <tt>true</tt> if we need to redirect in the handler or
         * <tt>false</tt> if we will continue with default redirect handling.
         */
        boolean hasParams(String location);
    }
}