From a58095aae10463a3063ef4aedc07474ed2c5aef1 Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Tue, 2 Feb 2010 02:06:29 +0000 Subject: [PATCH] URLEncode username and password for accounts, so as to not fall apart on nice simple characters like ':' in passwords. Patch by cketti Fixes Issue 1155. --- .../k9/activity/setup/AccountSetupBasics.java | 32 +++- .../activity/setup/AccountSetupIncoming.java | 31 +++- .../activity/setup/AccountSetupOutgoing.java | 19 +- src/com/fsck/k9/mail/store/ImapStore.java | 26 ++- src/com/fsck/k9/mail/store/Pop3Store.java | 16 +- src/com/fsck/k9/mail/store/WebDavStore.java | 33 ++-- .../fsck/k9/mail/transport/SmtpTransport.java | 21 ++- src/com/fsck/k9/preferences/Storage.java | 166 +++++++++++++++++- 8 files changed, 292 insertions(+), 52 deletions(-) diff --git a/src/com/fsck/k9/activity/setup/AccountSetupBasics.java b/src/com/fsck/k9/activity/setup/AccountSetupBasics.java index 66247260e..062769ae3 100644 --- a/src/com/fsck/k9/activity/setup/AccountSetupBasics.java +++ b/src/com/fsck/k9/activity/setup/AccountSetupBasics.java @@ -23,8 +23,12 @@ import android.widget.EditText; import com.fsck.k9.*; import java.io.Serializable; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; +import java.net.URLEncoder; + +import org.apache.http.client.utils.URLEncodedUtils; /** * Prompts the user for the email address and password. Also prompts for @@ -232,26 +236,34 @@ public class AccountSetupBasics extends K9Activity URI outgoingUri = null; try { + String userEnc = URLEncoder.encode(user, "UTF-8"); + String passwordEnc = URLEncoder.encode(password, "UTF-8"); + String incomingUsername = mProvider.incomingUsernameTemplate; incomingUsername = incomingUsername.replaceAll("\\$email", email); - incomingUsername = incomingUsername.replaceAll("\\$user", user); + incomingUsername = incomingUsername.replaceAll("\\$user", userEnc); incomingUsername = incomingUsername.replaceAll("\\$domain", domain); URI incomingUriTemplate = mProvider.incomingUriTemplate; incomingUri = new URI(incomingUriTemplate.getScheme(), incomingUsername + ":" - + password, incomingUriTemplate.getHost(), incomingUriTemplate.getPort(), null, + + passwordEnc, incomingUriTemplate.getHost(), incomingUriTemplate.getPort(), null, null, null); String outgoingUsername = mProvider.outgoingUsernameTemplate; outgoingUsername = outgoingUsername.replaceAll("\\$email", email); - outgoingUsername = outgoingUsername.replaceAll("\\$user", user); + outgoingUsername = outgoingUsername.replaceAll("\\$user", userEnc); outgoingUsername = outgoingUsername.replaceAll("\\$domain", domain); URI outgoingUriTemplate = mProvider.outgoingUriTemplate; outgoingUri = new URI(outgoingUriTemplate.getScheme(), outgoingUsername + ":" - + password, outgoingUriTemplate.getHost(), outgoingUriTemplate.getPort(), null, + + passwordEnc, outgoingUriTemplate.getHost(), outgoingUriTemplate.getPort(), null, null, null); } + catch (UnsupportedEncodingException enc) + { + // This really shouldn't happen since the encoding is hardcoded to UTF-8 + Log.e(K9.LOG_TAG, "Couldn't urlencode username or password.", enc); + } catch (URISyntaxException use) { /* @@ -277,9 +289,7 @@ public class AccountSetupBasics extends K9Activity private void onNext() { String email = mEmailView.getText().toString(); - String password = mPasswordView.getText().toString(); String[] emailParts = splitEmail(email); - String user = emailParts[0]; String domain = emailParts[1]; mProvider = findProviderForDomain(domain); if (mProvider == null) @@ -332,11 +342,19 @@ public class AccountSetupBasics extends K9Activity mAccount.setEmail(email); try { - URI uri = new URI("placeholder", user + ":" + password, "mail." + domain, -1, null, + String userEnc = URLEncoder.encode(user, "UTF-8"); + String passwordEnc = URLEncoder.encode(password, "UTF-8"); + + URI uri = new URI("placeholder", userEnc + ":" + passwordEnc, "mail." + domain, -1, null, null, null); mAccount.setStoreUri(uri.toString()); mAccount.setTransportUri(uri.toString()); } + catch (UnsupportedEncodingException enc) + { + // This really shouldn't happen since the encoding is hardcoded to UTF-8 + Log.e(K9.LOG_TAG, "Couldn't urlencode username or password.", enc); + } catch (URISyntaxException use) { /* diff --git a/src/com/fsck/k9/activity/setup/AccountSetupIncoming.java b/src/com/fsck/k9/activity/setup/AccountSetupIncoming.java index 2ac65c16e..c29025a09 100644 --- a/src/com/fsck/k9/activity/setup/AccountSetupIncoming.java +++ b/src/com/fsck/k9/activity/setup/AccountSetupIncoming.java @@ -14,8 +14,11 @@ import android.widget.*; import com.fsck.k9.*; import com.fsck.k9.activity.ChooseFolder; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; +import java.net.URLDecoder; +import java.net.URLEncoder; public class AccountSetupIncoming extends K9Activity implements OnClickListener { @@ -222,17 +225,17 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener if (userInfoParts.length == 3) { authType = userInfoParts[0]; - username = userInfoParts[1]; - password = userInfoParts[2]; + username = URLDecoder.decode(userInfoParts[1], "UTF-8"); + password = URLDecoder.decode(userInfoParts[2], "UTF-8"); } else if (userInfoParts.length == 2) { - username = userInfoParts[0]; - password = userInfoParts[1]; + username = URLDecoder.decode(userInfoParts[0], "UTF-8"); + password = URLDecoder.decode(userInfoParts[1], "UTF-8"); } else if (userInfoParts.length == 1) { - username = userInfoParts[0]; + username = URLDecoder.decode(userInfoParts[0], "UTF-8"); } } @@ -438,10 +441,12 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener */ try { + String usernameEnc = URLEncoder.encode(mUsernameView.getText().toString(), "UTF-8"); + String passwordEnc = URLEncoder.encode(mPasswordView.getText().toString(), "UTF-8"); URI oldUri = new URI(mAccount.getTransportUri()); URI uri = new URI( oldUri.getScheme(), - mUsernameView.getText() + ":" + mPasswordView.getText(), + usernameEnc + ":" + passwordEnc, oldUri.getHost(), oldUri.getPort(), null, @@ -449,6 +454,11 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener null); mAccount.setTransportUri(uri.toString()); } + catch (UnsupportedEncodingException enc) + { + // This really shouldn't happen since the encoding is hardcoded to UTF-8 + Log.e(K9.LOG_TAG, "Couldn't urlencode username or password.", enc); + } catch (URISyntaxException use) { /* @@ -482,14 +492,19 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener } final String userInfo; + String user = mUsernameView.getText().toString(); + String password = mPasswordView.getText().toString(); + String userEnc = URLEncoder.encode(user, "UTF-8"); + String passwordEnc = URLEncoder.encode(password, "UTF-8"); + if (mAccountSchemes[securityType].startsWith("imap")) { String authType = ((SpinnerOption)mAuthTypeView.getSelectedItem()).label; - userInfo = authType + ":" + mUsernameView.getText() + ":" + mPasswordView.getText(); + userInfo = authType + ":" + userEnc + ":" + passwordEnc; } else { - userInfo = mUsernameView.getText() + ":" + mPasswordView.getText(); + userInfo = userEnc + ":" + passwordEnc; } URI uri = new URI( mAccountSchemes[securityType], diff --git a/src/com/fsck/k9/activity/setup/AccountSetupOutgoing.java b/src/com/fsck/k9/activity/setup/AccountSetupOutgoing.java index c10bc82b6..621406fcf 100644 --- a/src/com/fsck/k9/activity/setup/AccountSetupOutgoing.java +++ b/src/com/fsck/k9/activity/setup/AccountSetupOutgoing.java @@ -15,8 +15,11 @@ import android.widget.*; import android.widget.CompoundButton.OnCheckedChangeListener; import com.fsck.k9.*; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; +import java.net.URLDecoder; +import java.net.URLEncoder; public class AccountSetupOutgoing extends K9Activity implements OnClickListener, OnCheckedChangeListener @@ -208,10 +211,11 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener, if (uri.getUserInfo() != null) { String[] userInfoParts = uri.getUserInfo().split(":"); - username = userInfoParts[0]; + + username = URLDecoder.decode(userInfoParts[0], "UTF-8"); if (userInfoParts.length > 1) { - password = userInfoParts[1]; + password = URLDecoder.decode(userInfoParts[1], "UTF-8"); } if (userInfoParts.length > 2) { @@ -325,18 +329,25 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener, URI uri; try { + String usernameEnc = URLEncoder.encode(mUsernameView.getText().toString(), "UTF-8"); + String passwordEnc = URLEncoder.encode(mPasswordView.getText().toString(), "UTF-8"); + String userInfo = null; String authType = ((SpinnerOption)mAuthTypeView.getSelectedItem()).label; if (mRequireLoginView.isChecked()) { - userInfo = mUsernameView.getText().toString() + ":" - + mPasswordView.getText().toString() + ":" + authType; + userInfo = usernameEnc + ":" + passwordEnc + ":" + authType; } uri = new URI(smtpSchemes[securityType], userInfo, mServerView.getText().toString(), Integer.parseInt(mPortView.getText().toString()), null, null, null); mAccount.setTransportUri(uri.toString()); AccountSetupCheckSettings.actionCheckSettings(this, mAccount, false, true); } + catch (UnsupportedEncodingException enc) + { + // This really shouldn't happen since the encoding is hardcoded to UTF-8 + Log.e(K9.LOG_TAG, "Couldn't urlencode username or password.", enc); + } catch (Exception e) { /* diff --git a/src/com/fsck/k9/mail/store/ImapStore.java b/src/com/fsck/k9/mail/store/ImapStore.java index 0eee510c8..d02d69d53 100644 --- a/src/com/fsck/k9/mail/store/ImapStore.java +++ b/src/com/fsck/k9/mail/store/ImapStore.java @@ -157,18 +157,26 @@ public class ImapStore extends Store if (uri.getUserInfo() != null) { - String[] userInfoParts = uri.getUserInfo().split(":"); - if (userInfoParts.length == 2) + try { - mAuthType = AuthType.PLAIN; - mUsername = userInfoParts[0]; - mPassword = userInfoParts[1]; + 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"); + } } - else + catch (UnsupportedEncodingException enc) { - mAuthType = AuthType.valueOf(userInfoParts[0]); - mUsername = userInfoParts[1]; - mPassword = userInfoParts[2]; + // This shouldn't happen since the encoding is hardcoded to UTF-8 + Log.e(K9.LOG_TAG, "Couldn't urldecode username or password.", enc); } } diff --git a/src/com/fsck/k9/mail/store/Pop3Store.java b/src/com/fsck/k9/mail/store/Pop3Store.java index bf937f535..d3d95ba1c 100644 --- a/src/com/fsck/k9/mail/store/Pop3Store.java +++ b/src/com/fsck/k9/mail/store/Pop3Store.java @@ -124,11 +124,19 @@ public class Pop3Store extends Store if (uri.getUserInfo() != null) { - String[] userInfoParts = uri.getUserInfo().split(":", 2); - mUsername = userInfoParts[0]; - if (userInfoParts.length > 1) + try { - mPassword = userInfoParts[1]; + String[] userInfoParts = uri.getUserInfo().split(":"); + mUsername = URLDecoder.decode(userInfoParts[0], "UTF-8"); + if (userInfoParts.length > 1) + { + mPassword = 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); } } } diff --git a/src/com/fsck/k9/mail/store/WebDavStore.java b/src/com/fsck/k9/mail/store/WebDavStore.java index 32bede54f..836caeb10 100644 --- a/src/com/fsck/k9/mail/store/WebDavStore.java +++ b/src/com/fsck/k9/mail/store/WebDavStore.java @@ -38,6 +38,7 @@ import javax.xml.parsers.SAXParserFactory; import java.io.*; import java.net.URI; import java.net.URISyntaxException; +import java.net.URLDecoder; import java.net.URLEncoder; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; @@ -202,21 +203,29 @@ public class WebDavStore extends Store if (mUri.getUserInfo() != null) { - String[] userInfoParts = mUri.getUserInfo().split(":", 2); - mUsername = userInfoParts[0]; - String userParts[] = mUsername.split("/", 2); + try + { + String[] userInfoParts = mUri.getUserInfo().split(":"); + mUsername = URLDecoder.decode(userInfoParts[0], "UTF-8"); + String userParts[] = mUsername.split("/", 2); - if (userParts.length > 1) - { - alias = userParts[1]; + if (userParts.length > 1) + { + alias = userParts[1]; + } + else + { + alias = mUsername; + } + if (userInfoParts.length > 1) + { + mPassword = URLDecoder.decode(userInfoParts[1], "UTF-8"); + } } - else + catch (UnsupportedEncodingException enc) { - alias = mUsername; - } - if (userInfoParts.length > 1) - { - mPassword = userInfoParts[1]; + // This shouldn't happen since the encoding is hardcoded to UTF-8 + Log.e(K9.LOG_TAG, "Couldn't urldecode username or password.", enc); } } mSecure = mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED; diff --git a/src/com/fsck/k9/mail/transport/SmtpTransport.java b/src/com/fsck/k9/mail/transport/SmtpTransport.java index 79a779e17..8eab801bd 100644 --- a/src/com/fsck/k9/mail/transport/SmtpTransport.java +++ b/src/com/fsck/k9/mail/transport/SmtpTransport.java @@ -16,6 +16,7 @@ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.io.UnsupportedEncodingException; import java.net.*; import java.security.GeneralSecurityException; import java.security.MessageDigest; @@ -118,15 +119,23 @@ public class SmtpTransport extends Transport if (uri.getUserInfo() != null) { - String[] userInfoParts = uri.getUserInfo().split(":"); - mUsername = userInfoParts[0]; - if (userInfoParts.length > 1) + try { - mPassword = userInfoParts[1]; + String[] userInfoParts = uri.getUserInfo().split(":"); + mUsername = URLDecoder.decode(userInfoParts[0], "UTF-8"); + if (userInfoParts.length > 1) + { + mPassword = URLDecoder.decode(userInfoParts[1], "UTF-8"); + } + if (userInfoParts.length > 2) + { + mAuthType = userInfoParts[2]; + } } - if (userInfoParts.length > 2) + catch (UnsupportedEncodingException enc) { - mAuthType = userInfoParts[2]; + // This shouldn't happen since the encoding is hardcoded to UTF-8 + Log.e(K9.LOG_TAG, "Couldn't urldecode username or password.", enc); } } } diff --git a/src/com/fsck/k9/preferences/Storage.java b/src/com/fsck/k9/preferences/Storage.java index e293855ca..d4101b164 100644 --- a/src/com/fsck/k9/preferences/Storage.java +++ b/src/com/fsck/k9/preferences/Storage.java @@ -6,8 +6,12 @@ import android.content.SharedPreferences; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.util.Log; -import com.fsck.k9.K9; +import com.fsck.k9.K9; +import com.fsck.k9.Utility; + +import java.net.URI; +import java.net.URLEncoder; import java.util.ArrayList; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -23,7 +27,7 @@ public class Storage implements SharedPreferences private CopyOnWriteArrayList listeners = new CopyOnWriteArrayList(); - private int DB_VERSION = 1; // CHANGING THIS WILL DESTROY ALL USER PREFERENCES! + private int DB_VERSION = 2; private String DB_NAME = "preferences_storage"; private ThreadLocal> workingStorage @@ -38,6 +42,116 @@ public class Storage implements SharedPreferences private SQLiteDatabase openDB() { SQLiteDatabase mDb = context.openOrCreateDatabase(DB_NAME, Context.MODE_PRIVATE, null); + + if (mDb.getVersion() == 1) + { + Log.i(K9.LOG_TAG, "Updating preferences to urlencoded username/password"); + + String accountUuids = readValue(mDb, "accountUuids"); + if (accountUuids != null && accountUuids.length() != 0) + { + String[] uuids = accountUuids.split(","); + for (int i = 0, length = uuids.length; i < length; i++) + { + String uuid = uuids[i]; + try + { + String storeUriStr = Utility.base64Decode(readValue(mDb, uuid + ".storeUri")); + String transportUriStr = Utility.base64Decode(readValue(mDb, uuid + ".transportUri")); + + URI uri = new URI(transportUriStr); + String newUserInfo = null; + if (transportUriStr != null) + { + String[] userInfoParts = uri.getUserInfo().split(":"); + + String usernameEnc = URLEncoder.encode(userInfoParts[0], "UTF-8"); + String passwordEnc = ""; + String authType = ""; + if (userInfoParts.length > 1) + { + passwordEnc = ":" + URLEncoder.encode(userInfoParts[1], "UTF-8"); + } + if (userInfoParts.length > 2) + { + authType = ":" + userInfoParts[2]; + } + + newUserInfo = usernameEnc + passwordEnc + authType; + } + + if (newUserInfo != null) + { + URI newUri = new URI(uri.getScheme(), newUserInfo, uri.getHost(), uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment()); + String newTransportUriStr = Utility.base64Encode(newUri.toString()); + writeValue(mDb, uuid + ".transportUri", newTransportUriStr); + } + + uri = new URI(storeUriStr); + newUserInfo = null; + if (storeUriStr.startsWith("imap")) + { + String[] userInfoParts = uri.getUserInfo().split(":"); + if (userInfoParts.length == 2) + { + String usernameEnc = URLEncoder.encode(userInfoParts[0], "UTF-8"); + String passwordEnc = URLEncoder.encode(userInfoParts[1], "UTF-8"); + + newUserInfo = usernameEnc + ":" + passwordEnc; + } + else + { + String authType = userInfoParts[0]; + String usernameEnc = URLEncoder.encode(userInfoParts[1], "UTF-8"); + String passwordEnc = URLEncoder.encode(userInfoParts[2], "UTF-8"); + + newUserInfo = authType + ":" + usernameEnc + ":" + passwordEnc; + } + } + else if (storeUriStr.startsWith("pop3")) + { + String[] userInfoParts = uri.getUserInfo().split(":", 2); + String usernameEnc = URLEncoder.encode(userInfoParts[0], "UTF-8"); + + String passwordEnc = ""; + if (userInfoParts.length > 1) + { + passwordEnc = ":" + URLEncoder.encode(userInfoParts[1], "UTF-8"); + } + + newUserInfo = usernameEnc + passwordEnc; + } + else if (storeUriStr.startsWith("webdav")) + { + String[] userInfoParts = uri.getUserInfo().split(":", 2); + String usernameEnc = URLEncoder.encode(userInfoParts[0], "UTF-8"); + + String passwordEnc = ""; + if (userInfoParts.length > 1) + { + passwordEnc = ":" + URLEncoder.encode(userInfoParts[1], "UTF-8"); + } + + newUserInfo = usernameEnc + passwordEnc; + } + + if (newUserInfo != null) + { + URI newUri = new URI(uri.getScheme(), newUserInfo, uri.getHost(), uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment()); + String newStoreUriStr = Utility.base64Encode(newUri.toString()); + writeValue(mDb, uuid + ".storeUri", newStoreUriStr); + } + } + catch (Exception e) + { + Log.e(K9.LOG_TAG, "ooops", e); + } + } + } + + mDb.setVersion(DB_VERSION); + } + if (mDb.getVersion() != DB_VERSION) { Log.i(K9.LOG_TAG, "Creating Storage database"); @@ -300,4 +414,52 @@ public class Storage implements SharedPreferences listeners.remove(listener); } + private String readValue(SQLiteDatabase mDb, String key) + { + Cursor cursor = null; + String value = null; + try + { + cursor = mDb.query( + "preferences_storage", + new String[] {"value"}, + "primkey = ?", + new String[] {key}, + null, + null, + null); + + if (cursor.moveToNext()) + { + value = cursor.getString(0); + if (K9.DEBUG) + { + Log.d(K9.LOG_TAG, "Loading key '" + key + "', value = '" + value + "'"); + } + } + } + finally + { + if (cursor != null) + { + cursor.close(); + } + } + + return value; + } + + private void writeValue(SQLiteDatabase mDb, String key, String value) + { + ContentValues cv = new ContentValues(); + cv.put("primkey", key); + cv.put("value", value); + + long result = mDb.insert("preferences_storage", "primkey", cv); + + if (result == -1) + { + Log.e(K9.LOG_TAG, "Error writing key '" + key + "', value = '" + value + "'"); + } + } }