k-9/k9mail/src/main/java/com/fsck/k9/activity/setup/AccountSetupBasics.java

535 lines
21 KiB
Java
Raw Normal View History

package com.fsck.k9.activity.setup;
2014-08-18 07:06:25 -04:00
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Locale;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.text.Editable;
2014-08-18 07:06:25 -04:00
import android.text.InputType;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
2014-05-25 16:45:14 -04:00
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
2014-05-25 16:45:14 -04:00
2014-08-18 07:06:25 -04:00
import com.fsck.k9.Account;
import com.fsck.k9.EmailAddressValidator;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.R;
import com.fsck.k9.activity.K9Activity;
Fix inadequate certificate validation Proper host name validation was not being performed for certificates kept in the local keystore. If an attacker could convince a user to accept and store an attacker's certificate, then that certificate could be used for MITM attacks, giving the attacker access to all connections to all servers in all accounts in K-9. This commit changes how the certificates are stored. Previously, an entire certificate chain was stored for a server (and any of those certificates in the chain were available for validating signatures on certificates received when connecting). Now just the single certificate for the server is stored. This commit changes how locally stored certificates are retrieved. They can only be retrieved using the host:port that the user configured for the server. This also fixes issue 1326. Users can now use different certificates for different servers on the same host (listening to different ports). The above changes mean that users might have to re-accept certificates that they had previously accepted and are still using (but only if the certificate's Subject doesn't match the host that they are connecting to). This commit modifies AccountSetupBasics so that it now calls AccountSetupCheckSettings twice -- once for checking the incoming settings and once for the outgoing settings. Otherwise, an exception could occur while checking incoming settings, the user could say continue (or the user could accept a certificate key), and the outgoing settings would not be checked. This also helps with determining if a certificate exception was for the incoming or outgoing server, which is needed if the user decides to add the certificate to the keystore.
2013-11-23 13:26:57 -05:00
import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection;
import com.fsck.k9.helper.UrlEncodingHelper;
import com.fsck.k9.helper.Utility;
2014-05-25 16:45:14 -04:00
import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Transport;
2014-12-18 03:33:09 -05:00
import com.fsck.k9.mail.store.imap.ImapStore;
import com.fsck.k9.mail.store.RemoteStore;
2014-05-25 16:45:14 -04:00
import com.fsck.k9.mail.transport.SmtpTransport;
import com.fsck.k9.view.ClientCertificateSpinner;
import com.fsck.k9.view.ClientCertificateSpinner.OnClientCertificateChangedListener;
/**
* Prompts the user for the email address and password.
* Attempts to lookup default settings for the domain the user specified. If the
* domain is known the settings are handed off to the AccountSetupCheckSettings
* activity. If no settings are found the settings are handed off to the
* AccountSetupAccountType activity.
*/
r62972@17h: jesse | 2009-05-07 10:49:32 -0400 First stab at a folderlist that doesn't know or care about messages r62973@17h: jesse | 2009-05-07 10:50:11 -0400 A very broken first stab at a message list that only knows about one folder. r62974@17h: jesse | 2009-05-07 10:50:44 -0400 When you go from an account list to an individual account, open a folderlist, not an fml r62975@17h: jesse | 2009-05-07 10:51:24 -0400 Update Welcome activity to open an ml instead of an fml r62976@17h: jesse | 2009-05-07 10:51:59 -0400 When setting up accounts is over, open an fl instead of an fml r62977@17h: jesse | 2009-05-07 10:52:51 -0400 Update MessageView to use folderinfoholders and messageinfoholders from the 'correct' classes. r62978@17h: jesse | 2009-05-07 10:59:07 -0400 MailService now notifies the fl instead of the fml. Not sure if it should also notify the ml. - will require testing r62979@17h: jesse | 2009-05-07 11:01:09 -0400 Switch MessagingController's notifications from notifying the FML to notifying an ML r62980@17h: jesse | 2009-05-07 11:25:22 -0400 Update AndroidManifest to know about the new world order r62981@17h: jesse | 2009-05-07 11:26:11 -0400 Try to follow the android sdk docs for intent creation r62982@17h: jesse | 2009-05-07 11:28:30 -0400 reset MessageList for another try at the conversion r62983@17h: jesse | 2009-05-07 11:47:33 -0400 This version doesn't crash and has a working 'folder' layer. now to clean up the message list layer r62984@17h: jesse | 2009-05-07 15:18:04 -0400 move step 1 r62985@17h: jesse | 2009-05-07 15:18:37 -0400 move step 1 r62986@17h: jesse | 2009-05-07 15:22:47 -0400 rename step 1 r62987@17h: jesse | 2009-05-07 17:38:02 -0400 checkpoint to move r62988@17h: jesse | 2009-05-07 17:40:01 -0400 checkpointing a state with a working folder list and a message list that doesn't explode r62989@17h: jesse | 2009-05-07 17:40:26 -0400 Remove debugging cruft from Welcome r62990@17h: jesse | 2009-05-07 22:00:12 -0400 Basic functionality works. r62991@17h: jesse | 2009-05-08 04:19:52 -0400 added a tool to build a K-9 "Beta" r62992@17h: jesse | 2009-05-08 04:20:03 -0400 remove a disused file r62993@17h: jesse | 2009-05-09 06:07:02 -0400 upgrading build infrastructure for the 1.5 sdk r62994@17h: jesse | 2009-05-09 06:22:02 -0400 further refine onOpenMessage, removing more folder assumptions r62995@17h: jesse | 2009-05-09 20:07:20 -0400 Make the Welcome activity open the autoexpandfolder rather than INBOX r62996@17h: jesse | 2009-05-09 20:14:10 -0400 MessageList now stores the Folder name it was working with across pause-reload r62997@17h: jesse | 2009-05-09 20:14:26 -0400 Removing dead code from FolderList r63060@17h: jesse | 2009-05-10 00:07:33 -0400 Replace the old message list refreshing code which cleared and rebuilt the list from scratch with code which updates or deletes existing messages. Add "go back to folder list" code r63061@17h: jesse | 2009-05-10 00:07:50 -0400 fix message list menus for new world order r63062@17h: jesse | 2009-05-10 00:08:11 -0400 Remove message list options from folder list menus r63063@17h: jesse | 2009-05-10 00:10:02 -0400 remove more message list options from the folder list r63064@17h: jesse | 2009-05-10 00:10:19 -0400 fix build.xml for the new android world order r63065@17h: jesse | 2009-05-10 00:39:23 -0400 reformatted in advance of bug tracing r63066@17h: jesse | 2009-05-10 05:53:28 -0400 fix our 'close' behavior to not leave extra activities around clean up more vestigal code r63067@17h: jesse | 2009-05-10 18:44:25 -0400 Improve "back button / accounts" workflow from FolderList -> AccountList r63068@17h: jesse | 2009-05-10 19:11:47 -0400 * Add required code for the 'k9beta' build r63069@17h: jesse | 2009-05-10 19:12:05 -0400 Make the folder list white backgrounded. r63070@17h: jesse | 2009-05-10 19:12:26 -0400 * Include our required libraries in build.xml r63071@17h: jesse | 2009-05-10 19:13:07 -0400 Added directories for our built code and our generated code r63072@17h: jesse | 2009-05-10 19:13:36 -0400 Added a "back" button image r63073@17h: jesse | 2009-05-10 20:13:50 -0400 Switch next/prev buttons to triangles for I18N and eventual "more easy-to-hit buttons" win r63074@17h: jesse | 2009-05-10 20:17:18 -0400 Tidy Accounts.java for some perf hacking. r63081@17h: jesse | 2009-05-10 22:13:33 -0400 First pass reformatting of the MessagingController r63082@17h: jesse | 2009-05-10 23:50:28 -0400 MessageList now correctly updates when a background sync happens r63083@17h: jesse | 2009-05-10 23:50:53 -0400 Tidying FolderList r63084@17h: jesse | 2009-05-10 23:51:09 -0400 tidy r63085@17h: jesse | 2009-05-10 23:51:27 -0400 tidy r63086@17h: jesse | 2009-05-11 00:17:06 -0400 Properly update unread counts in the FolderList after sync r63087@17h: jesse | 2009-05-11 01:38:14 -0400 Minor refactoring for readability. replace a boolean with a constant. r63090@17h: jesse | 2009-05-11 02:58:31 -0400 now that the foreground of message lists is light, we don't need the light messagebox r63091@17h: jesse | 2009-05-11 17:15:02 -0400 Added a string for "back to folder list" r63092@17h: jesse | 2009-05-11 17:15:24 -0400 Added a message list header with a back button r63093@17h: jesse | 2009-05-11 17:15:54 -0400 Remove the "folder list" button from the options menu. no sense duplicating it r63094@17h: jesse | 2009-05-11 17:17:06 -0400 Refactored views, adding our replacement scrollable header r63184@17h: jesse | 2009-05-12 07:07:15 -0400 fix weird bug where message lists could show a header element for a child r63185@17h: jesse | 2009-05-12 07:08:12 -0400 Add new-style headers to folder lists. reimplement "get folder by name" to not use a bloody for loop r63211@17h: jesse | 2009-05-12 18:37:48 -0400 Restore the former glory of the "load more messages" widget. it still needs an overhaul r63296@17h: jesse | 2009-05-12 23:23:21 -0400 Get the indeterminate progress bar to show up again when you click "get more messages" r63297@17h: jesse | 2009-05-13 02:40:39 -0400 Fixed off-by-one errors in click and keybindings for messagelist r63298@17h: jesse | 2009-05-13 06:04:01 -0400 Put the folder title in the name of the folderSettings popup r63299@17h: jesse | 2009-05-13 06:04:49 -0400 Reformatting. Removing debug logging r63300@17h: jesse | 2009-05-13 06:05:32 -0400 Fixing "wrong item selected" bugs in the FolderList r63328@17h: jesse | 2009-05-13 13:20:00 -0400 Update MessageView for 1.5 r63329@17h: jesse | 2009-05-13 13:50:29 -0400 A couple fixes to "picking the right item" Titles on the message context menu r63330@17h: jesse | 2009-05-13 13:58:37 -0400 Added an "open" context menu item to the folder list r63347@17h: jesse | 2009-05-13 18:00:02 -0400 Try to get folderlists to sort in a stable way, so they jump around less in the ui r63349@17h: jesse | 2009-05-13 20:37:19 -0400 Switch to using non-message-passing based notifications for redisplay of message lists, cut down redisplay frequency to not overload the display r63432@17h: jesse | 2009-05-16 13:38:49 -0400 Android 1.5 no longer gives us apache.commons.codec by default and apache.commons.logging by default. Import them so we have em. There's probably something smarter to do here. r63438@17h: jesse | 2009-05-16 14:12:06 -0400 removed dead code r63439@17h: jesse | 2009-05-16 14:30:57 -0400 Minor tidy r63440@17h: jesse | 2009-05-16 14:39:34 -0400 First pass implementation making MessageList streamy for faster startup r63441@17h: jesse | 2009-05-16 21:57:41 -0400 There's no reason for the FolderList to list local messages r63442@17h: jesse | 2009-05-16 21:58:57 -0400 Switch to actually refreshing the message list after each item is loaded r63450@17h: jesse | 2009-05-16 22:34:18 -0400 Default to pulling items out of the LocalStore by date, descending. (since that's the uneditable default ordering) This makes our messages come out of the store in the order the user should see them r63451@17h: jesse | 2009-05-16 22:34:44 -0400 Set some new defaults for the FolderList r63452@17h: jesse | 2009-05-16 22:35:43 -0400 set some new message list item defaults r63456@17h: jesse | 2009-05-17 12:56:10 -0400 It's not clear that Pop and WebDav actually set us an InternalDate. I'd rather use that so that spam doesn't topsort. But I also want this to _work_ r63457@17h: jesse | 2009-05-17 12:56:47 -0400 actually check to make sure we have a message to remove before removing it. r63458@17h: jesse | 2009-05-17 13:10:07 -0400 Flip "security type" to before the port number, since changing security type is the thing more users are likely to know/care about and resets port number r63469@17h: jesse | 2009-05-17 18:42:39 -0400 Provisional fix for "see the FoldeRList twice" bug r63471@17h: jesse | 2009-05-17 20:47:41 -0400 Remove title bar from the message view r63544@17h: jesse | 2009-05-20 23:53:38 -0400 folderlist tidying before i dig into the jumpy ordering bug r63545@17h: jesse | 2009-05-20 23:56:00 -0400 Killing dead variables r63546@17h: jesse | 2009-05-21 00:58:36 -0400 make the whole title section clicky r63556@17h: jesse | 2009-05-21 01:48:13 -0400 Fix where we go when someone deletes a message r63558@17h: jesse | 2009-05-21 22:44:46 -0400 Working toward switchable themes r63563@17h: jesse | 2009-05-21 23:53:09 -0400 Make the MessageList's colors actually just inherit from the theme, rather than hardcoding black r63567@17h: jesse | 2009-05-22 10:14:13 -0400 Kill a now-redundant comment r63571@17h: jesse | 2009-05-22 19:43:30 -0400 further theme-independence work r63572@17h: jesse | 2009-05-22 19:55:23 -0400 gete -> get (typo fix) r63573@17h: jesse | 2009-05-22 22:48:49 -0400 First cut of a global prefs system as well as a theme preference. not that it works yet r63577@17h: jesse | 2009-05-24 14:49:52 -0400 Once a user has actually put in valid user credentials, start syncing mail and folders in the background instantly. This gives us a much better "new startup" experience r63578@17h: jesse | 2009-05-24 14:55:00 -0400 MessageList doesn't need FolderUpdateWorker r63579@17h: jesse | 2009-05-24 17:57:15 -0400 Fix "get message by uid" Switch to showing messages 10 by 10, rather than 1 by 1 for huge loadtime performance improvements r63587@17h: jesse | 2009-05-24 19:19:56 -0400 Cut down LocalMessage creation to not generate a MessageId or date formatter. r63589@17h: jesse | 2009-05-24 22:22:32 -0400 Switch to null-escaping email address boundaries, rather than a VERY expensive URL-encoding r63590@17h: jesse | 2009-05-24 22:23:21 -0400 Clean up our "auto-refresh the list when adding messages after a sync" r63593@17h: jesse | 2009-05-24 22:53:45 -0400 replace isDateToday with a "rolling 18 hour window" variant that's more likely to give the user a useful answer and is 30x faster. r63595@17h: jesse | 2009-05-24 23:54:14 -0400 When instantiating messges from the LocalStore, there's no need to clear headers before setting them, nor is there a need to set a generated message id r63596@17h: jesse | 2009-05-24 23:54:39 -0400 make an overridable setGeneratedMessageId r63597@17h: jesse | 2009-05-24 23:54:55 -0400 Remove new lies from comments r63598@17h: jesse | 2009-05-24 23:55:35 -0400 Replace insanely expensive message header "name" part quoting with something consistent and cheap that does its work on the way INTO the database r63605@17h: jesse | 2009-05-25 17:28:24 -0400 bring back the 1.1 sdk build.xml r63606@17h: jesse | 2009-05-25 22:32:11 -0400 Actually enable switchable themese and compilation on 1.1 r63692@17h: jesse | 2009-05-29 23:55:17 -0400 Switch back to having titles for folder and message lists. Restore auto-open-folder functionality r63694@17h: jesse | 2009-05-30 18:50:39 -0400 Remove several off-by-one errors introduced by yesterday's return to android titlebars r63696@17h: jesse | 2009-05-30 23:45:03 -0400 use convertView properly for performance and memory imrpovement in FolderList and MessageList r63698@17h: jesse | 2009-05-31 19:42:59 -0400 Switch to using background shading to indicate "not yet fetched" r63701@17h: jesse | 2009-05-31 21:28:47 -0400 Remving code we don't actually need these bits of apache commons on 1.1
2009-05-31 21:35:05 -04:00
public class AccountSetupBasics extends K9Activity
2014-05-25 16:45:14 -04:00
implements OnClickListener, TextWatcher, OnCheckedChangeListener, OnClientCertificateChangedListener {
private final static String EXTRA_ACCOUNT = "com.fsck.k9.AccountSetupBasics.account";
private final static int DIALOG_NOTE = 1;
private final static String STATE_KEY_PROVIDER =
"com.fsck.k9.AccountSetupBasics.provider";
private final static String STATE_KEY_CHECKED_INCOMING =
"com.fsck.k9.AccountSetupBasics.checkedIncoming";
private EditText mEmailView;
private EditText mPasswordView;
2014-05-25 16:45:14 -04:00
private CheckBox mClientCertificateCheckBox;
private ClientCertificateSpinner mClientCertificateSpinner;
private Button mNextButton;
private Button mManualSetupButton;
private Account mAccount;
private Provider mProvider;
private EmailAddressValidator mEmailValidator = new EmailAddressValidator();
Fix inadequate certificate validation Proper host name validation was not being performed for certificates kept in the local keystore. If an attacker could convince a user to accept and store an attacker's certificate, then that certificate could be used for MITM attacks, giving the attacker access to all connections to all servers in all accounts in K-9. This commit changes how the certificates are stored. Previously, an entire certificate chain was stored for a server (and any of those certificates in the chain were available for validating signatures on certificates received when connecting). Now just the single certificate for the server is stored. This commit changes how locally stored certificates are retrieved. They can only be retrieved using the host:port that the user configured for the server. This also fixes issue 1326. Users can now use different certificates for different servers on the same host (listening to different ports). The above changes mean that users might have to re-accept certificates that they had previously accepted and are still using (but only if the certificate's Subject doesn't match the host that they are connecting to). This commit modifies AccountSetupBasics so that it now calls AccountSetupCheckSettings twice -- once for checking the incoming settings and once for the outgoing settings. Otherwise, an exception could occur while checking incoming settings, the user could say continue (or the user could accept a certificate key), and the outgoing settings would not be checked. This also helps with determining if a certificate exception was for the incoming or outgoing server, which is needed if the user decides to add the certificate to the keystore.
2013-11-23 13:26:57 -05:00
private boolean mCheckedIncoming = false;
private CheckBox mShowPasswordCheckBox;
public static void actionNewAccount(Context context) {
Intent i = new Intent(context, AccountSetupBasics.class);
context.startActivity(i);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.account_setup_basics);
mEmailView = (EditText)findViewById(R.id.account_email);
mPasswordView = (EditText)findViewById(R.id.account_password);
2014-05-25 16:45:14 -04:00
mClientCertificateCheckBox = (CheckBox)findViewById(R.id.account_client_certificate);
mClientCertificateSpinner = (ClientCertificateSpinner)findViewById(R.id.account_client_certificate_spinner);
mNextButton = (Button)findViewById(R.id.next);
mManualSetupButton = (Button)findViewById(R.id.manual_setup);
mShowPasswordCheckBox = (CheckBox) findViewById(R.id.show_password);
mNextButton.setOnClickListener(this);
mManualSetupButton.setOnClickListener(this);
}
private void initializeViewListeners() {
mEmailView.addTextChangedListener(this);
mPasswordView.addTextChangedListener(this);
2014-05-25 16:45:14 -04:00
mClientCertificateCheckBox.setOnCheckedChangeListener(this);
mClientCertificateSpinner.setOnClientCertificateChangedListener(this);
mShowPasswordCheckBox.setOnCheckedChangeListener (new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
showPassword(isChecked);
}
});
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mAccount != null) {
outState.putString(EXTRA_ACCOUNT, mAccount.getUuid());
}
if (mProvider != null) {
outState.putSerializable(STATE_KEY_PROVIDER, mProvider);
}
outState.putBoolean(STATE_KEY_CHECKED_INCOMING, mCheckedIncoming);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState.containsKey(EXTRA_ACCOUNT)) {
String accountUuid = savedInstanceState.getString(EXTRA_ACCOUNT);
mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
}
if (savedInstanceState.containsKey(STATE_KEY_PROVIDER)) {
mProvider = (Provider) savedInstanceState.getSerializable(STATE_KEY_PROVIDER);
}
mCheckedIncoming = savedInstanceState.getBoolean(STATE_KEY_CHECKED_INCOMING);
updateViewVisibility(mClientCertificateCheckBox.isChecked());
showPassword(mShowPasswordCheckBox.isChecked());
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
/*
* We wait until now to initialize the listeners because we didn't want
* the OnCheckedChangeListener active while the
* mClientCertificateCheckBox state was being restored because it could
* trigger the pop-up of a ClientCertificateSpinner.chooseCertificate()
* dialog.
*/
initializeViewListeners();
validateFields();
}
public void afterTextChanged(Editable s) {
validateFields();
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
2014-05-25 16:45:14 -04:00
@Override
public void onClientCertificateChanged(String alias) {
validateFields();
}
/**
* Called when checking the client certificate CheckBox
*/
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
updateViewVisibility(isChecked);
validateFields();
// Have the user select (or confirm) the client certificate
2014-05-25 16:45:14 -04:00
if (isChecked) {
mClientCertificateSpinner.chooseCertificate();
}
}
private void updateViewVisibility(boolean usingCertificates) {
if (usingCertificates) {
2014-05-25 16:45:14 -04:00
// hide password fields, show client certificate spinner
mPasswordView.setVisibility(View.GONE);
mShowPasswordCheckBox.setVisibility(View.GONE);
2014-05-25 16:45:14 -04:00
mClientCertificateSpinner.setVisibility(View.VISIBLE);
} else {
// show password fields, hide client certificate spinner
mPasswordView.setVisibility(View.VISIBLE);
mShowPasswordCheckBox.setVisibility(View.VISIBLE);
2014-05-25 16:45:14 -04:00
mClientCertificateSpinner.setVisibility(View.GONE);
}
}
private void showPassword(boolean show) {
if (show) {
mPasswordView.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
} else {
mPasswordView.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
}
}
private void validateFields() {
2014-05-25 16:45:14 -04:00
boolean clientCertificateChecked = mClientCertificateCheckBox.isChecked();
String clientCertificateAlias = mClientCertificateSpinner.getAlias();
String email = mEmailView.getText().toString();
2014-05-25 16:45:14 -04:00
boolean valid = Utility.requiredFieldValid(mEmailView)
&& ((!clientCertificateChecked && Utility.requiredFieldValid(mPasswordView))
|| (clientCertificateChecked && clientCertificateAlias != null))
&& mEmailValidator.isValidAddressOnly(email);
2010-02-13 12:26:54 -05:00
mNextButton.setEnabled(valid);
mManualSetupButton.setEnabled(valid);
/*
* Dim the next button's icon to 50% if the button is disabled.
* TODO this can probably be done with a stateful drawable. Check into it.
* android:state_enabled
*/
Utility.setCompoundDrawablesAlpha(mNextButton, mNextButton.isEnabled() ? 255 : 128);
}
private String getOwnerName() {
String name = null;
try {
name = getDefaultAccountName();
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Could not get default account name", e);
}
if (name == null) {
name = "";
}
return name;
}
private String getDefaultAccountName() {
String name = null;
Account account = Preferences.getPreferences(this).getDefaultAccount();
if (account != null) {
name = account.getName();
}
return name;
}
@Override
public Dialog onCreateDialog(int id) {
if (id == DIALOG_NOTE) {
if (mProvider != null && mProvider.note != null) {
return new AlertDialog.Builder(this)
.setMessage(mProvider.note)
.setPositiveButton(
getString(R.string.okay_action),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finishAutoSetup();
}
})
.setNegativeButton(
getString(R.string.cancel_action),
null)
.create();
}
}
return null;
}
private void finishAutoSetup() {
String email = mEmailView.getText().toString();
String password = mPasswordView.getText().toString();
String[] emailParts = splitEmail(email);
String user = emailParts[0];
String domain = emailParts[1];
try {
String userEnc = UrlEncodingHelper.encodeUtf8(user);
String passwordEnc = UrlEncodingHelper.encodeUtf8(password);
String incomingUsername = mProvider.incomingUsernameTemplate;
incomingUsername = incomingUsername.replaceAll("\\$email", email);
incomingUsername = incomingUsername.replaceAll("\\$user", userEnc);
incomingUsername = incomingUsername.replaceAll("\\$domain", domain);
URI incomingUriTemplate = mProvider.incomingUriTemplate;
2015-03-06 17:08:34 -05:00
URI incomingUri = new URI(incomingUriTemplate.getScheme(), incomingUsername + ":" + passwordEnc,
incomingUriTemplate.getHost(), incomingUriTemplate.getPort(), null, null, null);
String outgoingUsername = mProvider.outgoingUsernameTemplate;
URI outgoingUriTemplate = mProvider.outgoingUriTemplate;
2015-03-06 17:08:34 -05:00
URI outgoingUri;
if (outgoingUsername != null) {
outgoingUsername = outgoingUsername.replaceAll("\\$email", email);
outgoingUsername = outgoingUsername.replaceAll("\\$user", userEnc);
outgoingUsername = outgoingUsername.replaceAll("\\$domain", domain);
outgoingUri = new URI(outgoingUriTemplate.getScheme(), outgoingUsername + ":"
+ passwordEnc, outgoingUriTemplate.getHost(), outgoingUriTemplate.getPort(), null,
null, null);
} else {
outgoingUri = new URI(outgoingUriTemplate.getScheme(),
null, outgoingUriTemplate.getHost(), outgoingUriTemplate.getPort(), null,
null, null);
}
2013-03-06 01:53:39 -05:00
if (mAccount == null) {
mAccount = Preferences.getPreferences(this).newAccount();
}
mAccount.setName(getOwnerName());
mAccount.setEmail(email);
mAccount.setStoreUri(incomingUri.toString());
mAccount.setTransportUri(outgoingUri.toString());
mAccount.setDraftsFolderName(getString(R.string.special_mailbox_name_drafts));
mAccount.setTrashFolderName(getString(R.string.special_mailbox_name_trash));
mAccount.setArchiveFolderName(getString(R.string.special_mailbox_name_archive));
// Yahoo! has a special folder for Spam, called "Bulk Mail".
if (incomingUriTemplate.getHost().toLowerCase(Locale.US).endsWith(".yahoo.com")) {
mAccount.setSpamFolderName("Bulk Mail");
} else {
mAccount.setSpamFolderName(getString(R.string.special_mailbox_name_spam));
}
mAccount.setSentFolderName(getString(R.string.special_mailbox_name_sent));
if (incomingUri.toString().startsWith("imap")) {
mAccount.setDeletePolicy(Account.DELETE_POLICY_ON_DELETE);
} else if (incomingUri.toString().startsWith("pop3")) {
mAccount.setDeletePolicy(Account.DELETE_POLICY_NEVER);
}
Fix inadequate certificate validation Proper host name validation was not being performed for certificates kept in the local keystore. If an attacker could convince a user to accept and store an attacker's certificate, then that certificate could be used for MITM attacks, giving the attacker access to all connections to all servers in all accounts in K-9. This commit changes how the certificates are stored. Previously, an entire certificate chain was stored for a server (and any of those certificates in the chain were available for validating signatures on certificates received when connecting). Now just the single certificate for the server is stored. This commit changes how locally stored certificates are retrieved. They can only be retrieved using the host:port that the user configured for the server. This also fixes issue 1326. Users can now use different certificates for different servers on the same host (listening to different ports). The above changes mean that users might have to re-accept certificates that they had previously accepted and are still using (but only if the certificate's Subject doesn't match the host that they are connecting to). This commit modifies AccountSetupBasics so that it now calls AccountSetupCheckSettings twice -- once for checking the incoming settings and once for the outgoing settings. Otherwise, an exception could occur while checking incoming settings, the user could say continue (or the user could accept a certificate key), and the outgoing settings would not be checked. This also helps with determining if a certificate exception was for the incoming or outgoing server, which is needed if the user decides to add the certificate to the keystore.
2013-11-23 13:26:57 -05:00
// Check incoming here. Then check outgoing in onActivityResult()
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.INCOMING);
} catch (URISyntaxException use) {
/*
* If there is some problem with the URI we give up and go on to
* manual setup.
*/
onManualSetup();
2010-08-30 23:58:33 -04:00
}
}
protected void onNext() {
if (mClientCertificateCheckBox.isChecked()) {
// Auto-setup doesn't support client certificates.
onManualSetup();
return;
}
String email = mEmailView.getText().toString();
String[] emailParts = splitEmail(email);
String domain = emailParts[1];
mProvider = findProviderForDomain(domain);
if (mProvider == null) {
/*
* We don't have default settings for this account, start the manual
* setup process.
*/
onManualSetup();
return;
}
if (mProvider.note != null) {
showDialog(DIALOG_NOTE);
} else {
finishAutoSetup();
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
Fix inadequate certificate validation Proper host name validation was not being performed for certificates kept in the local keystore. If an attacker could convince a user to accept and store an attacker's certificate, then that certificate could be used for MITM attacks, giving the attacker access to all connections to all servers in all accounts in K-9. This commit changes how the certificates are stored. Previously, an entire certificate chain was stored for a server (and any of those certificates in the chain were available for validating signatures on certificates received when connecting). Now just the single certificate for the server is stored. This commit changes how locally stored certificates are retrieved. They can only be retrieved using the host:port that the user configured for the server. This also fixes issue 1326. Users can now use different certificates for different servers on the same host (listening to different ports). The above changes mean that users might have to re-accept certificates that they had previously accepted and are still using (but only if the certificate's Subject doesn't match the host that they are connecting to). This commit modifies AccountSetupBasics so that it now calls AccountSetupCheckSettings twice -- once for checking the incoming settings and once for the outgoing settings. Otherwise, an exception could occur while checking incoming settings, the user could say continue (or the user could accept a certificate key), and the outgoing settings would not be checked. This also helps with determining if a certificate exception was for the incoming or outgoing server, which is needed if the user decides to add the certificate to the keystore.
2013-11-23 13:26:57 -05:00
if (!mCheckedIncoming) {
//We've successfully checked incoming. Now check outgoing.
mCheckedIncoming = true;
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.OUTGOING);
} else {
//We've successfully checked outgoing as well.
mAccount.setDescription(mAccount.getEmail());
mAccount.save(Preferences.getPreferences(this));
K9.setServicesEnabled(this);
AccountSetupNames.actionSetNames(this, mAccount);
finish();
}
}
}
private void onManualSetup() {
String email = mEmailView.getText().toString();
String[] emailParts = splitEmail(email);
String user = emailParts[0];
String domain = emailParts[1];
2014-05-25 16:45:14 -04:00
String password = null;
String clientCertificateAlias = null;
AuthType authenticationType;
2014-05-25 16:45:14 -04:00
if (mClientCertificateCheckBox.isChecked()) {
authenticationType = AuthType.EXTERNAL;
clientCertificateAlias = mClientCertificateSpinner.getAlias();
} else {
authenticationType = AuthType.PLAIN;
password = mPasswordView.getText().toString();
}
2013-03-06 01:53:39 -05:00
if (mAccount == null) {
mAccount = Preferences.getPreferences(this).newAccount();
}
mAccount.setName(getOwnerName());
mAccount.setEmail(email);
2014-05-25 16:45:14 -04:00
// set default uris
// NOTE: they will be changed again in AccountSetupAccountType!
ServerSettings storeServer = new ServerSettings(ImapStore.STORE_TYPE, "mail." + domain, -1,
ConnectionSecurity.SSL_TLS_REQUIRED, authenticationType, user, password, clientCertificateAlias);
ServerSettings transportServer = new ServerSettings(SmtpTransport.TRANSPORT_TYPE, "mail." + domain, -1,
ConnectionSecurity.SSL_TLS_REQUIRED, authenticationType, user, password, clientCertificateAlias);
String storeUri = RemoteStore.createStoreUri(storeServer);
2014-05-25 16:45:14 -04:00
String transportUri = Transport.createTransportUri(transportServer);
mAccount.setStoreUri(storeUri);
mAccount.setTransportUri(transportUri);
mAccount.setDraftsFolderName(getString(R.string.special_mailbox_name_drafts));
mAccount.setTrashFolderName(getString(R.string.special_mailbox_name_trash));
mAccount.setSentFolderName(getString(R.string.special_mailbox_name_sent));
mAccount.setArchiveFolderName(getString(R.string.special_mailbox_name_archive));
// Yahoo! has a special folder for Spam, called "Bulk Mail".
if (domain.endsWith(".yahoo.com")) {
mAccount.setSpamFolderName("Bulk Mail");
} else {
mAccount.setSpamFolderName(getString(R.string.special_mailbox_name_spam));
}
AccountSetupAccountType.actionSelectAccountType(this, mAccount, false);
finish();
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.next:
onNext();
break;
case R.id.manual_setup:
onManualSetup();
break;
}
}
/**
* Attempts to get the given attribute as a String resource first, and if it fails
* returns the attribute as a simple String value.
* @param xml
* @param name
* @return
*/
private String getXmlAttribute(XmlResourceParser xml, String name) {
int resId = xml.getAttributeResourceValue(null, name, 0);
if (resId == 0) {
return xml.getAttributeValue(null, name);
} else {
return getString(resId);
}
}
private Provider findProviderForDomain(String domain) {
try {
XmlResourceParser xml = getResources().getXml(R.xml.providers);
int xmlEventType;
Provider provider = null;
while ((xmlEventType = xml.next()) != XmlResourceParser.END_DOCUMENT) {
if (xmlEventType == XmlResourceParser.START_TAG
&& "provider".equals(xml.getName())
&& domain.equalsIgnoreCase(getXmlAttribute(xml, "domain"))) {
provider = new Provider();
provider.id = getXmlAttribute(xml, "id");
provider.label = getXmlAttribute(xml, "label");
provider.domain = getXmlAttribute(xml, "domain");
provider.note = getXmlAttribute(xml, "note");
} else if (xmlEventType == XmlResourceParser.START_TAG
&& "incoming".equals(xml.getName())
&& provider != null) {
provider.incomingUriTemplate = new URI(getXmlAttribute(xml, "uri"));
provider.incomingUsernameTemplate = getXmlAttribute(xml, "username");
} else if (xmlEventType == XmlResourceParser.START_TAG
&& "outgoing".equals(xml.getName())
&& provider != null) {
provider.outgoingUriTemplate = new URI(getXmlAttribute(xml, "uri"));
provider.outgoingUsernameTemplate = getXmlAttribute(xml, "username");
} else if (xmlEventType == XmlResourceParser.END_TAG
&& "provider".equals(xml.getName())
&& provider != null) {
return provider;
}
}
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Error while trying to load provider settings.", e);
}
return null;
}
private String[] splitEmail(String email) {
String[] retParts = new String[2];
String[] emailParts = email.split("@");
retParts[0] = (emailParts.length > 0) ? emailParts[0] : "";
retParts[1] = (emailParts.length > 1) ? emailParts[1] : "";
return retParts;
}
static class Provider implements Serializable {
private static final long serialVersionUID = 8511656164616538989L;
public String id;
public String label;
public String domain;
public URI incomingUriTemplate;
public String incomingUsernameTemplate;
public URI outgoingUriTemplate;
public String outgoingUsernameTemplate;
public String note;
}
2014-05-25 16:45:14 -04:00
}