1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-08-13 17:03:48 -04:00
k-9/src/com/fsck/k9/activity/setup/AccountSetupOutgoing.java
Joe Steele 3c025379d4 Rework validateFields() and updateViewFromAuthType()
Previously, it was possible to have "Require sign-in" unchecked and a
"Security = None" setting for the outgoing server and still not be able to
tap "Next" because of a hidden (and irrelevant) "Authentication = Client
certificate" setting.

Check that the user has actually chosen a client certificate in
AccountSetupOutgoing.validateFields().

Also, there's no need to clear the password and certificate fields when
hiding them.  The user may accidentally change settings and want to change
them back without wiping out the existing settings.
2014-07-13 16:02:21 -04:00

392 lines
15 KiB
Java

package com.fsck.k9.activity.setup;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.method.DigitsKeyListener;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.*;
import android.widget.CompoundButton.OnCheckedChangeListener;
import com.fsck.k9.*;
import com.fsck.k9.activity.K9Activity;
import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Transport;
import com.fsck.k9.mail.transport.SmtpTransport;
import com.fsck.k9.net.ssl.SslHelper;
import com.fsck.k9.view.ClientCertificateSpinner;
import com.fsck.k9.view.ClientCertificateSpinner.OnClientCertificateChangedListener;
import java.net.URI;
import java.net.URISyntaxException;
public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
OnCheckedChangeListener {
private static final String EXTRA_ACCOUNT = "account";
private static final String EXTRA_MAKE_DEFAULT = "makeDefault";
private static final String SMTP_PORT = "587";
private static final String SMTP_SSL_PORT = "465";
private EditText mUsernameView;
private EditText mPasswordView;
private ClientCertificateSpinner mClientCertificateSpinner;
private TextView mClientCertificateLabelView;
private TextView mPasswordLabelView;
private EditText mServerView;
private EditText mPortView;
private CheckBox mRequireLoginView;
private ViewGroup mRequireLoginSettingsView;
private Spinner mSecurityTypeView;
private Spinner mAuthTypeView;
private ArrayAdapter<AuthType> mAuthTypeAdapter;
private Button mNextButton;
private Account mAccount;
private boolean mMakeDefault;
public static void actionOutgoingSettings(Context context, Account account, boolean makeDefault) {
Intent i = new Intent(context, AccountSetupOutgoing.class);
i.putExtra(EXTRA_ACCOUNT, account.getUuid());
i.putExtra(EXTRA_MAKE_DEFAULT, makeDefault);
context.startActivity(i);
}
public static void actionEditOutgoingSettings(Context context, Account account) {
context.startActivity(intentActionEditOutgoingSettings(context, account));
}
public static Intent intentActionEditOutgoingSettings(Context context, Account account) {
Intent i = new Intent(context, AccountSetupOutgoing.class);
i.setAction(Intent.ACTION_EDIT);
i.putExtra(EXTRA_ACCOUNT, account.getUuid());
return i;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.account_setup_outgoing);
String accountUuid = getIntent().getStringExtra(EXTRA_ACCOUNT);
mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
try {
if (new URI(mAccount.getStoreUri()).getScheme().startsWith("webdav")) {
mAccount.setTransportUri(mAccount.getStoreUri());
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.OUTGOING);
}
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mUsernameView = (EditText)findViewById(R.id.account_username);
mPasswordView = (EditText)findViewById(R.id.account_password);
mClientCertificateSpinner = (ClientCertificateSpinner)findViewById(R.id.account_client_certificate_spinner);
mClientCertificateLabelView = (TextView)findViewById(R.id.account_client_certificate_label);
mPasswordLabelView = (TextView)findViewById(R.id.account_password_label);
mServerView = (EditText)findViewById(R.id.account_server);
mPortView = (EditText)findViewById(R.id.account_port);
mRequireLoginView = (CheckBox)findViewById(R.id.account_require_login);
mRequireLoginSettingsView = (ViewGroup)findViewById(R.id.account_require_login_settings);
mSecurityTypeView = (Spinner)findViewById(R.id.account_security_type);
mAuthTypeView = (Spinner)findViewById(R.id.account_auth_type);
mNextButton = (Button)findViewById(R.id.next);
mNextButton.setOnClickListener(this);
mRequireLoginView.setOnCheckedChangeListener(this);
mSecurityTypeView.setAdapter(ConnectionSecurity.getArrayAdapter(this));
mAuthTypeAdapter = AuthType.getArrayAdapter(this, SslHelper.isClientCertificateSupportAvailable());
mAuthTypeView.setAdapter(mAuthTypeAdapter);
mUsernameView.addTextChangedListener(validationTextWatcher);
mPasswordView.addTextChangedListener(validationTextWatcher);
mServerView.addTextChangedListener(validationTextWatcher);
mPortView.addTextChangedListener(validationTextWatcher);
mClientCertificateSpinner.setOnClientCertificateChangedListener(clientCertificateChangedListener);
mAuthTypeView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position,
long id) {
updateViewFromAuthType();
}
@Override
public void onNothingSelected(AdapterView<?> parent) { /* unused */ }
});
/*
* Only allow digits in the port field.
*/
mPortView.setKeyListener(DigitsKeyListener.getInstance("0123456789"));
//FIXME: get Account object again?
accountUuid = getIntent().getStringExtra(EXTRA_ACCOUNT);
mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
mMakeDefault = getIntent().getBooleanExtra(EXTRA_MAKE_DEFAULT, false);
/*
* If we're being reloaded we override the original account with the one
* we saved
*/
if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_ACCOUNT)) {
accountUuid = savedInstanceState.getString(EXTRA_ACCOUNT);
mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
}
try {
ServerSettings settings = Transport.decodeTransportUri(mAccount.getTransportUri());
updateAuthPlainTextFromSecurityType(settings.connectionSecurity);
// The first item is selected if settings.authenticationType is null or is not in mAuthTypeAdapter
int position = mAuthTypeAdapter.getPosition(settings.authenticationType);
mAuthTypeView.setSelection(position, false);
// Select currently configured security type
mSecurityTypeView.setSelection(settings.connectionSecurity.ordinal(), false);
if (settings.username != null) {
mUsernameView.setText(settings.username);
mRequireLoginView.setChecked(true);
}
if (settings.password != null) {
mPasswordView.setText(settings.password);
}
if (settings.clientCertificateAlias != null) {
mClientCertificateSpinner.setAlias(settings.clientCertificateAlias);
}
/*
* Updates the port when the user changes the security type. This allows
* us to show a reasonable default which the user can change.
*
* Note: It's important that we set the listener *after* an initial option has been
* selected by the code above. Otherwise the listener might be called after
* onCreate() has been processed and the current port value set later in this
* method is overridden with the default port for the selected security type.
*/
mSecurityTypeView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position,
long id) {
// this indirectly triggers validateFields because the port text is watched
updatePortFromSecurityType();
}
@Override
public void onNothingSelected(AdapterView<?> parent) { /* unused */ }
});
if (settings.host != null) {
mServerView.setText(settings.host);
}
if (settings.port != -1) {
mPortView.setText(Integer.toString(settings.port));
} else {
updatePortFromSecurityType();
}
validateFields();
} catch (Exception e) {
/*
* We should always be able to parse our own settings.
*/
failure(e);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(EXTRA_ACCOUNT, mAccount.getUuid());
}
/**
* Shows/hides password field and client certificate spinner
*/
private void updateViewFromAuthType() {
AuthType authType = (AuthType) mAuthTypeView.getSelectedItem();
boolean isAuthTypeExternal = AuthType.EXTERNAL.equals(authType);
if (isAuthTypeExternal) {
// hide password fields, show client certificate fields
mPasswordView.setVisibility(View.GONE);
mPasswordLabelView.setVisibility(View.GONE);
mClientCertificateLabelView.setVisibility(View.VISIBLE);
mClientCertificateSpinner.setVisibility(View.VISIBLE);
} else {
// show password fields, hide client certificate fields
mPasswordView.setVisibility(View.VISIBLE);
mPasswordLabelView.setVisibility(View.VISIBLE);
mClientCertificateLabelView.setVisibility(View.GONE);
mClientCertificateSpinner.setVisibility(View.GONE);
}
validateFields();
}
private void validateFields() {
AuthType authType = (AuthType) mAuthTypeView.getSelectedItem();
boolean isAuthTypeExternal = AuthType.EXTERNAL.equals(authType);
ConnectionSecurity connectionSecurity = (ConnectionSecurity) mSecurityTypeView.getSelectedItem();
boolean hasConnectionSecurity = (!connectionSecurity.equals(ConnectionSecurity.NONE));
boolean hasValidCertificateAlias = mClientCertificateSpinner.getAlias() != null;
boolean hasValidUserName = Utility.requiredFieldValid(mUsernameView);
boolean hasValidPasswordSettings = hasValidUserName
&& !isAuthTypeExternal
&& Utility.requiredFieldValid(mPasswordView);
boolean hasValidExternalAuthSettings = hasValidUserName
&& isAuthTypeExternal
&& hasConnectionSecurity
&& hasValidCertificateAlias;
mNextButton
.setEnabled(Utility.domainFieldValid(mServerView)
&& Utility.requiredFieldValid(mPortView)
&& (!mRequireLoginView.isChecked()
|| hasValidPasswordSettings || hasValidExternalAuthSettings));
Utility.setCompoundDrawablesAlpha(mNextButton, mNextButton.isEnabled() ? 255 : 128);
}
private void updatePortFromSecurityType() {
ConnectionSecurity securityType = (ConnectionSecurity) mSecurityTypeView.getSelectedItem();
mPortView.setText(getDefaultSmtpPort(securityType));
updateAuthPlainTextFromSecurityType(securityType);
}
private String getDefaultSmtpPort(ConnectionSecurity securityType) {
String port;
switch (securityType) {
case NONE:
case STARTTLS_REQUIRED:
port = SMTP_PORT;
break;
case SSL_TLS_REQUIRED:
port = SMTP_SSL_PORT;
break;
default:
port = "";
Log.e(K9.LOG_TAG, "Unhandled ConnectionSecurity type encountered");
}
return port;
}
private void updateAuthPlainTextFromSecurityType(ConnectionSecurity securityType) {
switch (securityType) {
case NONE:
AuthType.PLAIN.useInsecureText(true, mAuthTypeAdapter);
break;
default:
AuthType.PLAIN.useInsecureText(false, mAuthTypeAdapter);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (Intent.ACTION_EDIT.equals(getIntent().getAction())) {
mAccount.save(Preferences.getPreferences(this));
finish();
} else {
AccountSetupOptions.actionOptions(this, mAccount, mMakeDefault);
finish();
}
}
}
protected void onNext() {
ConnectionSecurity securityType = (ConnectionSecurity) mSecurityTypeView.getSelectedItem();
String uri;
String username = null;
String password = null;
String clientCertificateAlias = null;
AuthType authType = null;
if (mRequireLoginView.isChecked()) {
username = mUsernameView.getText().toString();
authType = (AuthType) mAuthTypeView.getSelectedItem();
if (AuthType.EXTERNAL.equals(authType)) {
clientCertificateAlias = mClientCertificateSpinner.getAlias();
} else {
password = mPasswordView.getText().toString();
}
}
String newHost = mServerView.getText().toString();
int newPort = Integer.parseInt(mPortView.getText().toString());
String type = SmtpTransport.TRANSPORT_TYPE;
ServerSettings server = new ServerSettings(type, newHost, newPort, securityType, authType, username, password, clientCertificateAlias);
uri = Transport.createTransportUri(server);
mAccount.deleteCertificate(newHost, newPort, CheckDirection.OUTGOING);
mAccount.setTransportUri(uri);
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.OUTGOING);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.next:
onNext();
break;
}
}
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mRequireLoginSettingsView.setVisibility(isChecked ? View.VISIBLE : View.GONE);
validateFields();
}
private void failure(Exception use) {
Log.e(K9.LOG_TAG, "Failure", use);
String toastText = getString(R.string.account_setup_bad_uri, use.getMessage());
Toast toast = Toast.makeText(getApplication(), toastText, Toast.LENGTH_LONG);
toast.show();
}
/*
* Calls validateFields() which enables or disables the Next button
* based on the fields' validity.
*/
TextWatcher validationTextWatcher = new TextWatcher() {
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) {
}
};
OnClientCertificateChangedListener clientCertificateChangedListener = new OnClientCertificateChangedListener() {
@Override
public void onClientCertificateChanged(String alias) {
validateFields();
}
};
}