From ad1e2caa9cee32a2a13ae99a9cdbacc5ca9f1e38 Mon Sep 17 00:00:00 2001 From: cketti Date: Wed, 3 Feb 2016 20:54:51 +0100 Subject: [PATCH] Extract code to encode/decode ImapStore URIs --- .../fsck/k9/mail/store/imap/ImapStore.java | 170 +----------------- .../mail/store/imap/ImapStoreUriCreator.java | 71 ++++++++ .../mail/store/imap/ImapStoreUriDecoder.java | 126 +++++++++++++ 3 files changed, 199 insertions(+), 168 deletions(-) create mode 100644 k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapStoreUriCreator.java create mode 100644 k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapStoreUriDecoder.java diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapStore.java b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapStore.java index 20aa1870b..4b41739af 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapStore.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapStore.java @@ -2,8 +2,6 @@ package com.fsck.k9.mail.store.imap; import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; @@ -34,7 +32,6 @@ import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.PushReceiver; import com.fsck.k9.mail.Pusher; import com.fsck.k9.mail.ServerSettings; -import com.fsck.k9.mail.ServerSettings.Type; import com.fsck.k9.mail.ssl.TrustedSocketFactory; import com.fsck.k9.mail.store.RemoteStore; import com.fsck.k9.mail.store.StoreConfig; @@ -42,8 +39,6 @@ import com.fsck.k9.mail.store.StoreConfig; import com.beetstra.jutf7.CharsetProvider; import static com.fsck.k9.mail.K9MailLib.LOG_TAG; -import static com.fsck.k9.mail.helper.UrlEncodingHelper.decodeUtf8; -import static com.fsck.k9.mail.helper.UrlEncodingHelper.encodeUtf8; /** @@ -67,173 +62,12 @@ public class ImapStore extends RemoteStore { private String mCombinedPrefix = null; private String mPathDelimiter = null; - /** - * Decodes an ImapStore URI. - * - *

Possible forms:

- *
-     * imap://auth:user:password@server:port ConnectionSecurity.NONE
-     * imap+tls+://auth:user:password@server:port ConnectionSecurity.STARTTLS_REQUIRED
-     * imap+ssl+://auth:user:password@server:port ConnectionSecurity.SSL_TLS_REQUIRED
-     * 
- * - * @param uri the store uri. NOTE: this method expects the userinfo part of the uri to be - * encoded twice, due to a bug in {@link #createUri(ServerSettings)}. - */ public static ImapStoreSettings decodeUri(String uri) { - String host; - int port; - ConnectionSecurity connectionSecurity; - AuthType authenticationType = null; - String username = null; - String password = null; - String clientCertificateAlias = null; - String pathPrefix = null; - boolean autoDetectNamespace = true; - - URI imapUri; - try { - imapUri = new URI(uri); - } catch (URISyntaxException use) { - throw new IllegalArgumentException("Invalid ImapStore URI", use); - } - - String scheme = imapUri.getScheme(); - /* - * Currently available schemes are: - * imap - * imap+tls+ - * imap+ssl+ - * - * The following are obsolete schemes that may be found in pre-existing - * settings from earlier versions or that may be found when imported. We - * continue to recognize them and re-map them appropriately: - * imap+tls - * imap+ssl - */ - if (scheme.equals("imap")) { - connectionSecurity = ConnectionSecurity.NONE; - port = Type.IMAP.defaultPort; - } else if (scheme.startsWith("imap+tls")) { - connectionSecurity = ConnectionSecurity.STARTTLS_REQUIRED; - port = Type.IMAP.defaultPort; - } else if (scheme.startsWith("imap+ssl")) { - connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED; - port = Type.IMAP.defaultTlsPort; - } else { - throw new IllegalArgumentException("Unsupported protocol (" + scheme + ")"); - } - - host = imapUri.getHost(); - - if (imapUri.getPort() != -1) { - port = imapUri.getPort(); - } - - if (imapUri.getUserInfo() != null) { - String userinfo = imapUri.getUserInfo(); - String[] userInfoParts = userinfo.split(":"); - - if (userinfo.endsWith(":")) { - // Password is empty. This can only happen after an account was imported. - authenticationType = AuthType.valueOf(userInfoParts[0]); - username = decodeUtf8(userInfoParts[1]); - } else if (userInfoParts.length == 2) { - authenticationType = AuthType.PLAIN; - username = decodeUtf8(userInfoParts[0]); - password = decodeUtf8(userInfoParts[1]); - } else if (userInfoParts.length == 3) { - authenticationType = AuthType.valueOf(userInfoParts[0]); - username = decodeUtf8(userInfoParts[1]); - - if (AuthType.EXTERNAL == authenticationType) { - clientCertificateAlias = decodeUtf8(userInfoParts[2]); - } else { - password = decodeUtf8(userInfoParts[2]); - } - } - } - - String path = imapUri.getPath(); - if (path != null && path.length() > 1) { - // Strip off the leading "/" - String cleanPath = path.substring(1); - - if (cleanPath.length() >= 2 && cleanPath.charAt(1) == '|') { - autoDetectNamespace = cleanPath.charAt(0) == '1'; - if (!autoDetectNamespace) { - pathPrefix = cleanPath.substring(2); - } - } else { - if (cleanPath.length() > 0) { - pathPrefix = cleanPath; - autoDetectNamespace = false; - } - } - } - - return new ImapStoreSettings(host, port, connectionSecurity, authenticationType, username, - password, clientCertificateAlias, autoDetectNamespace, pathPrefix); + return ImapStoreUriDecoder.decode(uri); } - /** - * Creates an ImapStore URI with the supplied settings. - * - * @param server - * The {@link ServerSettings} object that holds the server settings. - * - * @return An ImapStore URI that holds the same information as the {@code server} parameter. - * - * @see com.fsck.k9.mail.store.StoreConfig#getStoreUri() - * @see ImapStore#decodeUri(String) - */ public static String createUri(ServerSettings server) { - String userEnc = encodeUtf8(server.username); - String passwordEnc = (server.password != null) ? - encodeUtf8(server.password) : ""; - String clientCertificateAliasEnc = (server.clientCertificateAlias != null) ? - encodeUtf8(server.clientCertificateAlias) : ""; - - String scheme; - switch (server.connectionSecurity) { - case SSL_TLS_REQUIRED: - scheme = "imap+ssl+"; - break; - case STARTTLS_REQUIRED: - scheme = "imap+tls+"; - break; - default: - case NONE: - scheme = "imap"; - break; - } - - AuthType authType = server.authenticationType; - String userInfo; - if (authType == AuthType.EXTERNAL) { - userInfo = authType.name() + ":" + userEnc + ":" + clientCertificateAliasEnc; - } else { - userInfo = authType.name() + ":" + userEnc + ":" + passwordEnc; - } - try { - Map extra = server.getExtra(); - String path; - if (extra != null) { - boolean autoDetectNamespace = Boolean.TRUE.toString().equals( - extra.get(ImapStoreSettings.AUTODETECT_NAMESPACE_KEY)); - String pathPrefix = (autoDetectNamespace) ? - null : extra.get(ImapStoreSettings.PATH_PREFIX_KEY); - path = "/" + (autoDetectNamespace ? "1" : "0") + "|" + - ((pathPrefix == null) ? "" : pathPrefix); - } else { - path = "/1|"; - } - return new URI(scheme, userInfo, server.host, server.port, - path, - null, null).toString(); - } catch (URISyntaxException e) { - throw new IllegalArgumentException("Can't create ImapStore URI", e); - } + return ImapStoreUriCreator.create(server); } diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapStoreUriCreator.java b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapStoreUriCreator.java new file mode 100644 index 000000000..b5d9ced48 --- /dev/null +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapStoreUriCreator.java @@ -0,0 +1,71 @@ +package com.fsck.k9.mail.store.imap; + + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; + +import com.fsck.k9.mail.AuthType; +import com.fsck.k9.mail.ServerSettings; + +import static com.fsck.k9.mail.helper.UrlEncodingHelper.encodeUtf8; + + +class ImapStoreUriCreator { + /** + * Creates an ImapStore URI with the supplied settings. + * + * @param server + * The {@link ServerSettings} object that holds the server settings. + * + * @return An ImapStore URI that holds the same information as the {@code server} parameter. + * + * @see com.fsck.k9.mail.store.StoreConfig#getStoreUri() + * @see ImapStore#decodeUri(String) + */ + public static String create(ServerSettings server) { + String userEnc = encodeUtf8(server.username); + String passwordEnc = (server.password != null) ? encodeUtf8(server.password) : ""; + String clientCertificateAliasEnc = (server.clientCertificateAlias != null) ? + encodeUtf8(server.clientCertificateAlias) : ""; + + String scheme; + switch (server.connectionSecurity) { + case SSL_TLS_REQUIRED: + scheme = "imap+ssl+"; + break; + case STARTTLS_REQUIRED: + scheme = "imap+tls+"; + break; + default: + case NONE: + scheme = "imap"; + break; + } + + AuthType authType = server.authenticationType; + String userInfo; + if (authType == AuthType.EXTERNAL) { + userInfo = authType.name() + ":" + userEnc + ":" + clientCertificateAliasEnc; + } else { + userInfo = authType.name() + ":" + userEnc + ":" + passwordEnc; + } + try { + Map extra = server.getExtra(); + String path; + if (extra != null) { + boolean autoDetectNamespace = Boolean.TRUE.toString().equals( + extra.get(ImapStoreSettings.AUTODETECT_NAMESPACE_KEY)); + String pathPrefix = (autoDetectNamespace) ? + null : extra.get(ImapStoreSettings.PATH_PREFIX_KEY); + path = "/" + (autoDetectNamespace ? "1" : "0") + "|" + + ((pathPrefix == null) ? "" : pathPrefix); + } else { + path = "/1|"; + } + return new URI(scheme, userInfo, server.host, server.port, path, null, null).toString(); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Can't create ImapStore URI", e); + } + } +} diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapStoreUriDecoder.java b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapStoreUriDecoder.java new file mode 100644 index 000000000..9014bded8 --- /dev/null +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapStoreUriDecoder.java @@ -0,0 +1,126 @@ +package com.fsck.k9.mail.store.imap; + + +import java.net.URI; +import java.net.URISyntaxException; + +import com.fsck.k9.mail.AuthType; +import com.fsck.k9.mail.ConnectionSecurity; +import com.fsck.k9.mail.ServerSettings; +import com.fsck.k9.mail.ServerSettings.Type; + +import static com.fsck.k9.mail.helper.UrlEncodingHelper.decodeUtf8; + + +class ImapStoreUriDecoder { + /** + * Decodes an ImapStore URI. + * + *

Possible forms:

+ *
+     * imap://auth:user:password@server:port ConnectionSecurity.NONE
+     * imap+tls+://auth:user:password@server:port ConnectionSecurity.STARTTLS_REQUIRED
+     * imap+ssl+://auth:user:password@server:port ConnectionSecurity.SSL_TLS_REQUIRED
+     * 
+ * + * NOTE: this method expects the userinfo part of the uri to be encoded twice, due to a bug in + * {@link ImapStoreUriCreator#create(ServerSettings)}. + * + * @param uri the store uri. + */ + public static ImapStoreSettings decode(String uri) { + String host; + int port; + ConnectionSecurity connectionSecurity; + AuthType authenticationType = null; + String username = null; + String password = null; + String clientCertificateAlias = null; + String pathPrefix = null; + boolean autoDetectNamespace = true; + + URI imapUri; + try { + imapUri = new URI(uri); + } catch (URISyntaxException use) { + throw new IllegalArgumentException("Invalid ImapStore URI", use); + } + + String scheme = imapUri.getScheme(); + /* + * Currently available schemes are: + * imap + * imap+tls+ + * imap+ssl+ + * + * The following are obsolete schemes that may be found in pre-existing + * settings from earlier versions or that may be found when imported. We + * continue to recognize them and re-map them appropriately: + * imap+tls + * imap+ssl + */ + if (scheme.equals("imap")) { + connectionSecurity = ConnectionSecurity.NONE; + port = Type.IMAP.defaultPort; + } else if (scheme.startsWith("imap+tls")) { + connectionSecurity = ConnectionSecurity.STARTTLS_REQUIRED; + port = Type.IMAP.defaultPort; + } else if (scheme.startsWith("imap+ssl")) { + connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED; + port = Type.IMAP.defaultTlsPort; + } else { + throw new IllegalArgumentException("Unsupported protocol (" + scheme + ")"); + } + + host = imapUri.getHost(); + + if (imapUri.getPort() != -1) { + port = imapUri.getPort(); + } + + if (imapUri.getUserInfo() != null) { + String userinfo = imapUri.getUserInfo(); + String[] userInfoParts = userinfo.split(":"); + + if (userinfo.endsWith(":")) { + // Password is empty. This can only happen after an account was imported. + authenticationType = AuthType.valueOf(userInfoParts[0]); + username = decodeUtf8(userInfoParts[1]); + } else if (userInfoParts.length == 2) { + authenticationType = AuthType.PLAIN; + username = decodeUtf8(userInfoParts[0]); + password = decodeUtf8(userInfoParts[1]); + } else if (userInfoParts.length == 3) { + authenticationType = AuthType.valueOf(userInfoParts[0]); + username = decodeUtf8(userInfoParts[1]); + + if (AuthType.EXTERNAL == authenticationType) { + clientCertificateAlias = decodeUtf8(userInfoParts[2]); + } else { + password = decodeUtf8(userInfoParts[2]); + } + } + } + + String path = imapUri.getPath(); + if (path != null && path.length() > 1) { + // Strip off the leading "/" + String cleanPath = path.substring(1); + + if (cleanPath.length() >= 2 && cleanPath.charAt(1) == '|') { + autoDetectNamespace = cleanPath.charAt(0) == '1'; + if (!autoDetectNamespace) { + pathPrefix = cleanPath.substring(2); + } + } else { + if (cleanPath.length() > 0) { + pathPrefix = cleanPath; + autoDetectNamespace = false; + } + } + } + + return new ImapStoreSettings(host, port, connectionSecurity, authenticationType, username, + password, clientCertificateAlias, autoDetectNamespace, pathPrefix); + } +}