Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 29738993 authored by cketti's avatar cketti
Browse files

Added function to decode store URIs into a container object

This will later be used by the export code to make exporting the
password optional (and the XML output "pretty").
It's also the first step to get away from store URIs towards something
more easily extensible, like Store.StoreSettings.
parent 0e2afc38
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
package com.fsck.k9.mail;

/**
 * The currently available connection security types.
 *
 * <p>
 * Right now this enum is only used by {@link Store.StoreSettings} and converted to store-specific
 * constants in the different Store implementations. In the future we probably want to change this
 * and use {@code ConnectionSecurity} exclusively.
 * </p>
 */
public enum ConnectionSecurity {
    NONE,
    STARTTLS_OPTIONAL,
    STARTTLS_REQUIRED,
    SSL_TLS_OPTIONAL,
    SSL_TLS_REQUIRED
}
+112 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ import com.fsck.k9.mail.store.StorageManager.StorageProvider;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Store is the access point for an email message store. It's location can be
@@ -83,6 +84,117 @@ public abstract class Store {
        return (LocalStore) store;
    }

    /**
     * Decodes the contents of store-specific URIs and puts them into a {@link StoreSettings}
     * object.
     *
     * @param uri
     *         the store-specific URI to decode
     *
     * @return A {@link StoreSettings} object holding the settings contained in the URI.
     *
     * @see ImapStore#decodeUri(String)
     * @see Pop3Store#decodeUri(String)
     * @see WebDavStore#decodeUri(String)
     */
    public static StoreSettings decodeStoreUri(String uri) {
        if (uri.startsWith("imap")) {
            return ImapStore.decodeUri(uri);
        } else if (uri.startsWith("pop3")) {
            return Pop3Store.decodeUri(uri);
        } else if (uri.startsWith("webdav")) {
            return WebDavStore.decodeUri(uri);
        } else {
            throw new IllegalArgumentException("Not a valid store URI");
        }
    }

    /**
     * This is an abstraction to get rid of the store-specific URIs.
     *
     * <p>
     * Right now it's only used for settings import/export. But the goal is to get rid of
     * store URIs altogether.
     * </p>
     *
     * @see Account#getStoreUri()
     */
    public static class StoreSettings {
        /**
         * The host name of the incoming server.
         */
        public final String host;

        /**
         * The port number of the incoming server.
         */
        public final int port;

        /**
         * The type of connection security to be used when connecting to the incoming server.
         *
         * {@link ConnectionSecurity#NONE} if not applicable for the store.
         */
        public final ConnectionSecurity connectionSecurity;

        /**
         * The authentication method to use when connecting to the incoming server.
         *
         * {@code null} if not applicable for the store.
         */
        public final String authenticationType;

        /**
         * The username part of the credentials needed to authenticate to the incoming server.
         *
         * {@code null} if unused or not applicable for the store.
         */
        public final String username;

        /**
         * The password part of the credentials needed to authenticate to the incoming server.
         *
         * {@code null} if unused or not applicable for the store.
         */
        public final String password;


        /**
         * Creates a new {@code StoreSettings} object.
         *
         * @param host
         *         see {@link StoreSettings#host}
         * @param port
         *         see {@link StoreSettings#port}
         * @param connectionSecurity
         *         see {@link StoreSettings#connectionSecurity}
         * @param authenticationType
         *         see {@link StoreSettings#authenticationType}
         * @param username
         *         see {@link StoreSettings#username}
         * @param password
         *         see {@link StoreSettings#password}
         */
        public StoreSettings(String host, int port, ConnectionSecurity connectionSecurity,
                String authenticationType, String username, String password) {
            this.host = host;
            this.port = port;
            this.connectionSecurity = connectionSecurity;
            this.authenticationType = authenticationType;
            this.username = username;
            this.password = password;
        }

        /**
         * Returns store-specific settings as key/value pair.
         *
         * <p>Classes that inherit from this one are expected to override this method.</p>
         */
        public Map<String, String> getExtra() {
            return null;
        }
    }


    protected final Account mAccount;

+134 −56
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock;
import com.fsck.k9.mail.AuthenticationFailedException;
import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.CertificateValidationException;
import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.FetchProfile;
import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Folder;
@@ -132,6 +133,115 @@ public class ImapStore extends Store {

    private static final String[] EMPTY_STRING_ARRAY = new String[0];

    /**
     * Decodes an ImapStore URI.
     *
     * <p>Possible forms:</p>
     * <pre>
     * imap://auth:user:password@server:port CONNECTION_SECURITY_NONE
     * imap+tls://auth:user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
     * imap+tls+://auth:user:password@server:port CONNECTION_SECURITY_TLS_REQUIRED
     * imap+ssl+://auth:user:password@server:port CONNECTION_SECURITY_SSL_REQUIRED
     * imap+ssl://auth:user:password@server:port CONNECTION_SECURITY_SSL_OPTIONAL
     * </pre>
     */
    public static ImapStoreSettings decodeUri(String uri) {
        String host;
        int port;
        ConnectionSecurity connectionSecurity;
        String authenticationType = null;
        String username = null;
        String password = null;
        String pathPrefix = null;

        URI imapUri;
        try {
            imapUri = new URI(uri);
        } catch (URISyntaxException use) {
            throw new IllegalArgumentException("Invalid ImapStore URI", use);
        }

        String scheme = imapUri.getScheme();
        if (scheme.equals("imap")) {
            connectionSecurity = ConnectionSecurity.NONE;
            port = 143;
        } else if (scheme.equals("imap+tls")) {
            connectionSecurity = ConnectionSecurity.STARTTLS_OPTIONAL;
            port = 143;
        } else if (scheme.equals("imap+tls+")) {
            connectionSecurity = ConnectionSecurity.STARTTLS_REQUIRED;
            port = 143;
        } else if (scheme.equals("imap+ssl+")) {
            connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED;
            port = 993;
        } else if (scheme.equals("imap+ssl")) {
            connectionSecurity = ConnectionSecurity.SSL_TLS_OPTIONAL;
            port = 993;
        } else {
            throw new IllegalArgumentException("Unsupported protocol (" + scheme + ")");
        }

        host = imapUri.getHost();

        if (imapUri.getPort() != -1) {
            port = imapUri.getPort();
        }

        if (imapUri.getUserInfo() != null) {
            try {
                String[] userInfoParts = imapUri.getUserInfo().split(":");
                if (userInfoParts.length == 2) {
                    authenticationType = AuthType.PLAIN.name();
                    username = URLDecoder.decode(userInfoParts[0], "UTF-8");
                    password = URLDecoder.decode(userInfoParts[1], "UTF-8");
                } else {
                    authenticationType = AuthType.valueOf(userInfoParts[0]).name();
                    username = URLDecoder.decode(userInfoParts[1], "UTF-8");
                    password = URLDecoder.decode(userInfoParts[2], "UTF-8");
                }
            } catch (UnsupportedEncodingException enc) {
                // This shouldn't happen since the encoding is hardcoded to UTF-8
                throw new IllegalArgumentException("Couldn't urldecode username or password.", enc);
            }
        }

        String path = imapUri.getPath();
        if (path != null && path.length() > 0) {
            pathPrefix = path.substring(1);
            if (pathPrefix != null && pathPrefix.trim().length() == 0) {
                pathPrefix = null;
            }
        }

        return new ImapStoreSettings(host, port, connectionSecurity, authenticationType, username,
                password, pathPrefix);
    }

    /**
     * This class is used to store the decoded contents of an ImapStore URI.
     *
     * @see ImapStore#decodeUri(String)
     */
    private static class ImapStoreSettings extends StoreSettings {
        private static final String PATH_PREFIX_KEY = "path_prefix";

        public final String pathPrefix;

        protected ImapStoreSettings(String host, int port, ConnectionSecurity connectionSecurity,
                String authenticationType, String username, String password, String pathPrefix) {
            super(host, port, connectionSecurity, authenticationType, username, password);
            this.pathPrefix = pathPrefix;
        }

        @Override
        public Map<String, String> getExtra() {
            Map<String, String> extra = new HashMap<String, String>();
            extra.put(PATH_PREFIX_KEY, pathPrefix);
            return extra;
        }
    }


    private String mHost;
    private int mPort;
    private String mUsername;
@@ -229,74 +339,42 @@ public class ImapStore extends Store {
     */
    private HashMap<String, ImapFolder> mFolderCache = new HashMap<String, ImapFolder>();

    /**
     * imap://auth:user:password@server:port CONNECTION_SECURITY_NONE
     * imap+tls://auth:user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
     * imap+tls+://auth:user:password@server:port CONNECTION_SECURITY_TLS_REQUIRED
     * imap+ssl+://auth:user:password@server:port CONNECTION_SECURITY_SSL_REQUIRED
     * imap+ssl://auth:user:password@server:port CONNECTION_SECURITY_SSL_OPTIONAL
     *
     * @param _uri
     */
    public ImapStore(Account account) throws MessagingException {
        super(account);
        URI uri;

        ImapStoreSettings settings;
        try {
            uri = new URI(mAccount.getStoreUri());
        } catch (URISyntaxException use) {
            throw new MessagingException("Invalid ImapStore URI", use);
            settings = decodeUri(mAccount.getStoreUri());
        } catch (IllegalArgumentException e) {
            throw new MessagingException("Error while decoding store URI", e);
        }

        String scheme = uri.getScheme();
        if (scheme.equals("imap")) {
        mHost = settings.host;
        mPort = settings.port;

        switch (settings.connectionSecurity) {
        case NONE:
            mConnectionSecurity = CONNECTION_SECURITY_NONE;
            mPort = 143;
        } else if (scheme.equals("imap+tls")) {
            break;
        case STARTTLS_OPTIONAL:
            mConnectionSecurity = CONNECTION_SECURITY_TLS_OPTIONAL;
            mPort = 143;
        } else if (scheme.equals("imap+tls+")) {
            break;
        case STARTTLS_REQUIRED:
            mConnectionSecurity = CONNECTION_SECURITY_TLS_REQUIRED;
            mPort = 143;
        } else if (scheme.equals("imap+ssl+")) {
            mConnectionSecurity = CONNECTION_SECURITY_SSL_REQUIRED;
            mPort = 993;
        } else if (scheme.equals("imap+ssl")) {
            break;
        case SSL_TLS_OPTIONAL:
            mConnectionSecurity = CONNECTION_SECURITY_SSL_OPTIONAL;
            mPort = 993;
        } else {
            throw new MessagingException("Unsupported protocol");
        }

        mHost = uri.getHost();

        if (uri.getPort() != -1) {
            mPort = uri.getPort();
            break;
        case SSL_TLS_REQUIRED:
            mConnectionSecurity = CONNECTION_SECURITY_SSL_REQUIRED;
            break;
        }

        if (uri.getUserInfo() != null) {
            try {
                String[] userInfoParts = uri.getUserInfo().split(":");
                if (userInfoParts.length == 2) {
                    mAuthType = AuthType.PLAIN;
                    mUsername = URLDecoder.decode(userInfoParts[0], "UTF-8");
                    mPassword = URLDecoder.decode(userInfoParts[1], "UTF-8");
                } else {
                    mAuthType = AuthType.valueOf(userInfoParts[0]);
                    mUsername = URLDecoder.decode(userInfoParts[1], "UTF-8");
                    mPassword = URLDecoder.decode(userInfoParts[2], "UTF-8");
                }
            } catch (UnsupportedEncodingException enc) {
                // This shouldn't happen since the encoding is hardcoded to UTF-8
                Log.e(K9.LOG_TAG, "Couldn't urldecode username or password.", enc);
            }
        }
        mAuthType = AuthType.valueOf(settings.authenticationType);
        mUsername = settings.username;
        mPassword = settings.password;

        if ((uri.getPath() != null) && (uri.getPath().length() > 0)) {
            mPathPrefix = uri.getPath().substring(1);
            if (mPathPrefix != null && mPathPrefix.trim().length() == 0) {
                mPathPrefix = null;
            }
        }
        mPathPrefix = settings.pathPrefix;

        mModifiedUtf7Charset = new CharsetProvider().charsetForName("X-RFC-3501");
    }
+82 −34
Original line number Diff line number Diff line
@@ -34,69 +34,117 @@ public class Pop3Store extends Store {

    private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED };

    private String mHost;
    private int mPort;
    private String mUsername;
    private String mPassword;
    private int mConnectionSecurity;
    private HashMap<String, Folder> mFolders = new HashMap<String, Folder>();
    private Pop3Capabilities mCapabilities;

    /**
     * Decodes a Pop3Store URI.
     *
     * <p>Possible forms:</p>
     * <pre>
     * pop3://user:password@server:port CONNECTION_SECURITY_NONE
     * pop3+tls://user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
     * pop3+tls+://user:password@server:port CONNECTION_SECURITY_TLS_REQUIRED
     * pop3+ssl+://user:password@server:port CONNECTION_SECURITY_SSL_REQUIRED
     * pop3+ssl://user:password@server:port CONNECTION_SECURITY_SSL_OPTIONAL
     * </pre>
     */
    public Pop3Store(Account account) throws MessagingException {
        super(account);

        URI uri;
    public static StoreSettings decodeUri(String uri) {
        String host;
        int port;
        ConnectionSecurity connectionSecurity;
        String username = null;
        String password = null;

        URI pop3Uri;
        try {
            uri = new URI(mAccount.getStoreUri());
            pop3Uri = new URI(uri);
        } catch (URISyntaxException use) {
            throw new MessagingException("Invalid Pop3Store URI", use);
            throw new IllegalArgumentException("Invalid Pop3Store URI", use);
        }

        String scheme = uri.getScheme();
        String scheme = pop3Uri.getScheme();
        if (scheme.equals("pop3")) {
            mConnectionSecurity = CONNECTION_SECURITY_NONE;
            mPort = 110;
            connectionSecurity = ConnectionSecurity.NONE;
            port = 110;
        } else if (scheme.equals("pop3+tls")) {
            mConnectionSecurity = CONNECTION_SECURITY_TLS_OPTIONAL;
            mPort = 110;
            connectionSecurity = ConnectionSecurity.STARTTLS_OPTIONAL;
            port = 110;
        } else if (scheme.equals("pop3+tls+")) {
            mConnectionSecurity = CONNECTION_SECURITY_TLS_REQUIRED;
            mPort = 110;
            connectionSecurity = ConnectionSecurity.STARTTLS_REQUIRED;
            port = 110;
        } else if (scheme.equals("pop3+ssl+")) {
            mConnectionSecurity = CONNECTION_SECURITY_SSL_REQUIRED;
            mPort = 995;
            connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED;
            port = 995;
        } else if (scheme.equals("pop3+ssl")) {
            mConnectionSecurity = CONNECTION_SECURITY_SSL_OPTIONAL;
            mPort = 995;
            connectionSecurity = ConnectionSecurity.SSL_TLS_OPTIONAL;
            port = 995;
        } else {
            throw new MessagingException("Unsupported protocol");
            throw new IllegalArgumentException("Unsupported protocol (" + scheme + ")");
        }

        mHost = uri.getHost();
        host = pop3Uri.getHost();

        if (uri.getPort() != -1) {
            mPort = uri.getPort();
        if (pop3Uri.getPort() != -1) {
            port = pop3Uri.getPort();
        }

        if (uri.getUserInfo() != null) {
        if (pop3Uri.getUserInfo() != null) {
            try {
                String[] userInfoParts = uri.getUserInfo().split(":");
                mUsername = URLDecoder.decode(userInfoParts[0], "UTF-8");
                String[] userInfoParts = pop3Uri.getUserInfo().split(":");
                username = URLDecoder.decode(userInfoParts[0], "UTF-8");
                if (userInfoParts.length > 1) {
                    mPassword = URLDecoder.decode(userInfoParts[1], "UTF-8");
                    password = URLDecoder.decode(userInfoParts[1], "UTF-8");
                }
            } catch (UnsupportedEncodingException enc) {
                // This shouldn't happen since the encoding is hardcoded to UTF-8
                Log.e(K9.LOG_TAG, "Couldn't urldecode username or password.", enc);
                throw new IllegalArgumentException("Couldn't urldecode username or password.", enc);
            }
        }

        return new StoreSettings(host, port, connectionSecurity, null, username, password);
    }


    private String mHost;
    private int mPort;
    private String mUsername;
    private String mPassword;
    private int mConnectionSecurity;
    private HashMap<String, Folder> mFolders = new HashMap<String, Folder>();
    private Pop3Capabilities mCapabilities;


    public Pop3Store(Account account) throws MessagingException {
        super(account);

        StoreSettings settings;
        try {
            settings = decodeUri(mAccount.getStoreUri());
        } catch (IllegalArgumentException e) {
            throw new MessagingException("Error while decoding store URI", e);
        }

        mHost = settings.host;
        mPort = settings.port;

        switch (settings.connectionSecurity) {
        case NONE:
            mConnectionSecurity = CONNECTION_SECURITY_NONE;
            break;
        case STARTTLS_OPTIONAL:
            mConnectionSecurity = CONNECTION_SECURITY_TLS_OPTIONAL;
            break;
        case STARTTLS_REQUIRED:
            mConnectionSecurity = CONNECTION_SECURITY_TLS_REQUIRED;
            break;
        case SSL_TLS_OPTIONAL:
            mConnectionSecurity = CONNECTION_SECURITY_SSL_OPTIONAL;
            break;
        case SSL_TLS_REQUIRED:
            mConnectionSecurity = CONNECTION_SECURITY_SSL_REQUIRED;
            break;
        }

        mUsername = settings.username;
        mPassword = settings.password;
    }

    @Override
+152 −49

File changed.

Preview size limit exceeded, changes collapsed.