ch.cyberduck.core.gdocs.GDSession.java Source code

Java tutorial

Introduction

Here is the source code for ch.cyberduck.core.gdocs.GDSession.java

Source

package ch.cyberduck.core.gdocs;

/*
 * Copyright (c) 2002-2010 David Kocher. All rights reserved.
 *
 * http://cyberduck.ch/
 *
 * 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 2 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.
 *
 * Bug fixes, suggestions and comments should be sent to:
 * dkocher@cyberduck.ch
 */

import ch.cyberduck.core.*;
import ch.cyberduck.core.Proxy;
import ch.cyberduck.core.i18n.Locale;
import ch.cyberduck.core.ssl.CustomTrustSSLProtocolSocketFactory;
import ch.cyberduck.core.ssl.SSLSession;

import org.apache.commons.lang.StringUtils;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import com.google.gdata.client.ClientLoginAccountType;
import com.google.gdata.client.GoogleAuthTokenFactory;
import com.google.gdata.client.GoogleService;
import com.google.gdata.client.docs.DocsService;
import com.google.gdata.client.http.GoogleGDataRequest;
import com.google.gdata.client.http.HttpUrlConnectionSource;
import com.google.gdata.data.acl.AclRole;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.common.base.CharEscapers;
import com.google.gdata.util.common.base.StringUtil;

/**
 *
 */
public class GDSession extends SSLSession {
    private static org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(GDSession.class);

    private static class Factory extends SessionFactory {
        @Override
        protected Session create(Host h) {
            return new GDSession(h);
        }
    }

    public static SessionFactory factory() {
        return new Factory();
    }

    private DocsService client;

    public GDSession(Host h) {
        super(h);
    }

    @Override
    protected DocsService getClient() throws ConnectionCanceledException {
        if (null == client) {
            throw new ConnectionCanceledException();
        }
        return client;
    }

    private final Handler appender = new Handler() {
        @Override
        public void publish(LogRecord record) {
            GDSession.this.log(false, record.getMessage());
        }

        @Override
        public void flush() {
            ;
        }

        @Override
        public void close() throws SecurityException {
            ;
        }
    };

    private static final Logger http = Logger.getLogger("com.google.gdata.client.http.HttpGDataRequest");

    @Override
    protected void fireConnectionWillOpenEvent() throws ResolveCanceledException, UnknownHostException {
        http.setLevel(Level.FINER);
        http.addHandler(appender);
        super.fireConnectionWillOpenEvent();
    }

    @Override
    protected void fireConnectionWillCloseEvent() {
        final java.util.logging.Logger logger = java.util.logging.Logger
                .getLogger("com.google.gdata.client.http.HttpGDataRequest");
        logger.removeHandler(appender);
        super.fireConnectionWillCloseEvent();
    }

    @Override
    protected void connect() throws IOException {
        if (this.isConnected()) {
            return;
        }
        this.fireConnectionWillOpenEvent();

        GoogleGDataRequest.Factory requestFactory = new GoogleGDataRequest.Factory();
        requestFactory.setConnectionSource(new HttpUrlConnectionSource() {
            public HttpURLConnection openConnection(URL url) throws IOException {
                return getConnection(url);
            }
        });
        GoogleAuthTokenFactory authFactory = new GoogleAuthTokenFactory(DocsService.DOCS_SERVICE,
                this.getUserAgent(), host.getProtocol().getScheme(), "www.google.com", client) {

            @Override
            public String getAuthToken(String username, String password, String captchaToken, String captchaAnswer,
                    String serviceName, String applicationName, ClientLoginAccountType accountType)
                    throws AuthenticationException {

                Map<String, String> params = new HashMap<String, String>();
                params.put("Email", username);
                params.put("Passwd", password);
                params.put("source", applicationName);
                params.put("service", serviceName);
                params.put("accountType", accountType.getValue());

                if (captchaToken != null) {
                    params.put("logintoken", captchaToken);
                }
                if (captchaAnswer != null) {
                    params.put("logincaptcha", captchaAnswer);
                }
                String postOutput;
                try {
                    URL url = new URL(
                            host.getProtocol().getScheme() + "://" + "www.google.com" + GOOGLE_LOGIN_PATH);
                    postOutput = request(url, params);
                } catch (IOException e) {
                    AuthenticationException ae = new AuthenticationException(
                            Locale.localizedString("Connection failed", "Error"));
                    ae.initCause(e);
                    throw ae;
                }

                // Parse the output
                Map<String, String> tokenPairs = StringUtil.string2Map(postOutput.trim(), "\n", "=", true);
                String token = tokenPairs.get("Auth");
                if (token == null) {
                    throw getAuthException(tokenPairs);
                }
                return token;
            }

            /**
             * Returns the respective {@code AuthenticationException} given the return
             * values from the login URI handler.
             *
             * @param pairs name/value pairs returned as a result of a bad authentication
             * @return the respective {@code AuthenticationException} for the given error
             */
            private AuthenticationException getAuthException(Map<String, String> pairs) {

                String errorName = pairs.get("Error");

                if ("BadAuthentication".equals(errorName)) {
                    return new GoogleService.InvalidCredentialsException("Invalid credentials");

                } else if ("AccountDeleted".equals(errorName)) {
                    return new GoogleService.AccountDeletedException("Account deleted");

                } else if ("AccountDisabled".equals(errorName)) {
                    return new GoogleService.AccountDisabledException("Account disabled");

                } else if ("NotVerified".equals(errorName)) {
                    return new GoogleService.NotVerifiedException("Not verified");

                } else if ("TermsNotAgreed".equals(errorName)) {
                    return new GoogleService.TermsNotAgreedException("Terms not agreed");

                } else if ("ServiceUnavailable".equals(errorName)) {
                    return new GoogleService.ServiceUnavailableException("Service unavailable");

                } else if ("CaptchaRequired".equals(errorName)) {

                    String captchaPath = pairs.get("CaptchaUrl");
                    StringBuilder captchaUrl = new StringBuilder();
                    captchaUrl.append(host.getProtocol().getScheme()).append("://").append("www.google.com")
                            .append(GOOGLE_ACCOUNTS_PATH).append('/').append(captchaPath);
                    return new GoogleService.CaptchaRequiredException("Captcha required", captchaUrl.toString(),
                            pairs.get("CaptchaToken"));

                } else {
                    return new AuthenticationException("Error authenticating " + "(check service name)");
                }
            }

            /**
             * Makes a HTTP POST request to the provided {@code url} given the
             * provided {@code parameters}.  It returns the output from the POST
             * handler as a String object.
             *
             * @param url the URL to post the request
             * @param parameters the parameters to post to the handler
             * @return the output from the handler
             * @throws IOException if an I/O exception occurs while creating, writing,
             *                     or reading the request
             */
            private String request(URL url, Map<String, String> parameters) throws IOException {

                // Open connection
                HttpURLConnection urlConnection = getConnection(url);

                // Set properties of the connection
                urlConnection.setDoInput(true);
                urlConnection.setDoOutput(true);
                urlConnection.setUseCaches(false);
                urlConnection.setRequestMethod("POST");
                urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

                // Form the POST parameters
                StringBuilder content = new StringBuilder();
                boolean first = true;
                for (Map.Entry<String, String> parameter : parameters.entrySet()) {
                    if (!first) {
                        content.append("&");
                    }
                    content.append(CharEscapers.uriEscaper().escape(parameter.getKey())).append("=");
                    content.append(CharEscapers.uriEscaper().escape(parameter.getValue()));
                    first = false;
                }

                OutputStream outputStream = null;
                try {
                    outputStream = urlConnection.getOutputStream();
                    outputStream.write(content.toString().getBytes("utf-8"));
                    outputStream.flush();
                } finally {
                    if (outputStream != null) {
                        outputStream.close();
                    }
                }

                // Retrieve the output
                InputStream inputStream = null;
                StringBuilder outputBuilder = new StringBuilder();
                try {
                    int responseCode = urlConnection.getResponseCode();
                    if (responseCode == HttpURLConnection.HTTP_OK) {
                        inputStream = urlConnection.getInputStream();
                    } else {
                        inputStream = urlConnection.getErrorStream();
                    }

                    String string;
                    if (inputStream != null) {
                        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                        while (null != (string = reader.readLine())) {
                            outputBuilder.append(string).append('\n');
                        }
                    }
                } finally {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                }
                return outputBuilder.toString();
            }
        };
        client = new DocsService(this.getUserAgent(), requestFactory, authFactory);
        client.setReadTimeout(this.timeout());
        client.setConnectTimeout(this.timeout());
        if (this.getHost().getProtocol().isSecure()) {
            client.useSsl();
        }

        if (!this.isConnected()) {
            throw new ConnectionCanceledException();
        }
        this.login();
        this.fireConnectionDidOpenEvent();
    }

    protected HttpURLConnection getConnection(URL url) throws IOException {
        URLConnection c = null;
        if (Preferences.instance().getBoolean("connection.proxy.enable")) {
            final Proxy proxy = ProxyFactory.instance();
            if (proxy.isHTTPSProxyEnabled(new Host(Protocol.GDOCS_SSL, url.getHost(), url.getPort()))) {
                c = url.openConnection(new java.net.Proxy(java.net.Proxy.Type.HTTP,
                        new InetSocketAddress(proxy.getHTTPSProxyHost(host), proxy.getHTTPSProxyPort(host))));
            }
        }
        if (null == c) {
            log.debug("Direct connection");
            c = url.openConnection();
        }
        c.setConnectTimeout(timeout());
        c.setReadTimeout(timeout());
        if (c instanceof HttpsURLConnection) {
            ((HttpsURLConnection) c).setSSLSocketFactory(
                    new CustomTrustSSLProtocolSocketFactory(this.getTrustManager(url.getHost())));
            ((HttpsURLConnection) c).setHostnameVerifier(new HostnameVerifier() {
                public boolean verify(String s, javax.net.ssl.SSLSession sslSession) {
                    return true;
                }
            });
        }
        if (c instanceof HttpURLConnection) {
            return (HttpURLConnection) c;
        }
        throw new ConnectionCanceledException("Invalid URL connection:" + c);
    }

    @Override
    public String getUserAgent() {
        return Preferences.instance().getProperty("application.name") + "-"
                + Preferences.instance().getProperty("application.version");
    }

    @Override
    protected void login(LoginController controller, Credentials credentials) throws IOException {
        try {
            this.getClient().setUserCredentials(credentials.getUsername(), credentials.getPassword());
            this.message(Locale.localizedString("Login successful", "Credentials"));
        } catch (AuthenticationException e) {
            this.message(Locale.localizedString("Login failed", "Credentials"));
            controller.fail(host.getProtocol(), credentials, e.getMessage());
            this.login();
        }
    }

    @Override
    public void close() {
        try {
            if (this.isConnected()) {
                this.fireConnectionWillCloseEvent();
            }
        } finally {
            // No logout required
            client = null;
            this.fireConnectionDidCloseEvent();
        }
    }

    /**
     * @return No Content-Range support
     */
    @Override
    public boolean isDownloadResumable() {
        return false;
    }

    /**
     * @return No Content-Range support
     */
    @Override
    public boolean isUploadResumable() {
        return false;
    }

    @Override
    public boolean isUnixPermissionsSupported() {
        return false;
    }

    @Override
    public boolean isAclSupported() {
        return true;
    }

    @Override
    public boolean isTimestampSupported() {
        return false;
    }

    @Override
    public List<Acl.Role> getAvailableAclRoles(List<Path> files) {
        return Arrays.asList(new Acl.Role(AclRole.OWNER.getValue(), false), new Acl.Role(AclRole.READER.getValue()),
                new Acl.Role(AclRole.WRITER.getValue()));
    }

    @Override
    public List<Acl.User> getAvailableAclUsers() {
        return Arrays.asList(new Acl.EmailUser(StringUtils.EMPTY) {
            @Override
            public String getPlaceholder() {
                return Locale.localizedString("Google Email Address", "Google");
            }
        }, new Acl.DomainUser(StringUtils.EMPTY) {
            @Override
            public String getPlaceholder() {
                return Locale.localizedString("Google Apps Domain", "Google");
            }
        }, new Acl.GroupUser(StringUtils.EMPTY, true) {
            @Override
            public String getPlaceholder() {
                return Locale.localizedString("Google Group Email Address", "Google");
            }
        }, new Acl.CanonicalUser(Locale.localizedString("Public", "Google"), false) {
            @Override
            public String getPlaceholder() {
                return Locale.localizedString("Public", "Google");
            }
        });
    }

    @Override
    public boolean isCDNSupported() {
        return false;
    }
}