Added certificate selection

pull/174/head
M66B 5 years ago
parent 8d3ec9aa43
commit 94a9e05da5

File diff suppressed because it is too large Load Diff

@ -60,7 +60,7 @@ import io.requery.android.database.sqlite.SQLiteDatabase;
// https://developer.android.com/topic/libraries/architecture/room.html // https://developer.android.com/topic/libraries/architecture/room.html
@Database( @Database(
version = 141, version = 142,
entities = { entities = {
EntityIdentity.class, EntityIdentity.class,
EntityAccount.class, EntityAccount.class,
@ -1364,6 +1364,14 @@ public abstract class DB extends RoomDatabase {
db.execSQL("ALTER TABLE `account` ADD COLUMN `certificate` INTEGER NOT NULL DEFAULT 0"); db.execSQL("ALTER TABLE `account` ADD COLUMN `certificate` INTEGER NOT NULL DEFAULT 0");
db.execSQL("ALTER TABLE `identity` ADD COLUMN `certificate` INTEGER NOT NULL DEFAULT 0"); db.execSQL("ALTER TABLE `identity` ADD COLUMN `certificate` INTEGER NOT NULL DEFAULT 0");
} }
})
.addMigrations(new Migration(141, 142) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase db) {
Log.i("DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `account` ADD COLUMN `certificate_alias` TEXT");
db.execSQL("ALTER TABLE `identity` ADD COLUMN `certificate_alias` TEXT");
}
}); });
} }

@ -5,6 +5,7 @@ import android.accounts.AccountManager;
import android.accounts.AuthenticatorException; import android.accounts.AuthenticatorException;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.security.KeyChain;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -35,10 +36,8 @@ import java.net.Socket;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.Principal; import java.security.Principal;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException; import java.security.cert.CertificateParsingException;
@ -240,7 +239,7 @@ public class EmailService implements AutoCloseable {
account.host, account.port, account.host, account.port,
account.auth_type, account.provider, account.auth_type, account.provider,
account.user, account.password, account.user, account.password,
account.certificate, account.fingerprint); account.certificate_alias, account.fingerprint);
if (password != null) { if (password != null) {
DB db = DB.getInstance(context); DB db = DB.getInstance(context);
int count = db.account().setAccountPassword(account.id, account.password); int count = db.account().setAccountPassword(account.id, account.password);
@ -253,7 +252,7 @@ public class EmailService implements AutoCloseable {
identity.host, identity.port, identity.host, identity.port,
identity.auth_type, identity.provider, identity.auth_type, identity.provider,
identity.user, identity.password, identity.user, identity.password,
identity.certificate, identity.fingerprint); identity.certificate_alias, identity.fingerprint);
if (password != null) { if (password != null) {
DB db = DB.getInstance(context); DB db = DB.getInstance(context);
int count = db.identity().setIdentityPassword(identity.id, identity.password); int count = db.identity().setIdentityPassword(identity.id, identity.password);
@ -264,10 +263,20 @@ public class EmailService implements AutoCloseable {
public String connect( public String connect(
String host, int port, String host, int port,
int auth, String provider, String user, String password, int auth, String provider, String user, String password,
boolean certificate, String fingerprint) throws MessagingException { String certificate, String fingerprint) throws MessagingException {
SSLSocketFactoryService factory = null; SSLSocketFactoryService factory = null;
try { try {
factory = new SSLSocketFactoryService(host, insecure, harden, certificate, fingerprint); X509Certificate[] certs = null;
if (certificate != null) {
Log.i("Get client certificate alias=" + certificate);
try {
certs = KeyChain.getCertificateChain(context, certificate);
} catch (Throwable ex) {
Log.w(ex);
}
}
factory = new SSLSocketFactoryService(host, insecure, harden, certs, fingerprint);
properties.put("mail." + protocol + ".ssl.socketFactory", factory); properties.put("mail." + protocol + ".ssl.socketFactory", factory);
properties.put("mail." + protocol + ".socketFactory.fallback", "false"); properties.put("mail." + protocol + ".socketFactory.fallback", "false");
properties.put("mail." + protocol + ".ssl.checkserveridentity", "false"); properties.put("mail." + protocol + ".ssl.checkserveridentity", "false");
@ -576,7 +585,7 @@ public class EmailService implements AutoCloseable {
private SSLSocketFactory factory; private SSLSocketFactory factory;
private X509Certificate certificate; private X509Certificate certificate;
SSLSocketFactoryService(String host, boolean insecure, boolean harden, boolean use_certificate, String fingerprint) throws GeneralSecurityException { SSLSocketFactoryService(String host, boolean insecure, boolean harden, X509Certificate[] certs, String fingerprint) throws GeneralSecurityException {
this.server = host; this.server = host;
this.secure = !insecure; this.secure = !insecure;
this.harden = harden; this.harden = harden;
@ -646,19 +655,15 @@ public class EmailService implements AutoCloseable {
}; };
KeyManager[] km = null; KeyManager[] km = null;
if (use_certificate) if (certs != null)
try { try {
Log.i("Client certificate init"); Log.i("Client certificate init certs=" + certs.length);
KeyStore ca = KeyStore.getInstance("AndroidCAStore");
ca.load(null, null);
Certificate cert = ca.getCertificate(server);
if (cert == null)
throw new KeyStoreException("Certificate not found host=" + server);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null); ks.load(null, null);
ks.setCertificateEntry(server, cert);
for (int i = 0; i < certs.length; i++)
ks.setCertificateEntry(server + ":" + i, certs[i]);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, null); kmf.init(ks, null);

@ -78,7 +78,8 @@ public class EntityAccount extends EntityOrder implements Serializable {
@NonNull @NonNull
public String password; public String password;
@NonNull @NonNull
public Boolean certificate; public Boolean certificate; // obsolete
public String certificate_alias;
public String realm; public String realm;
public String fingerprint; public String fingerprint;
@ -197,7 +198,7 @@ public class EntityAccount extends EntityOrder implements Serializable {
json.put("provider", provider); json.put("provider", provider);
json.put("user", user); json.put("user", user);
json.put("password", password); json.put("password", password);
json.put("certificate", certificate); json.put("certificate_alias", certificate_alias);
json.put("realm", realm); json.put("realm", realm);
json.put("fingerprint", fingerprint); json.put("fingerprint", fingerprint);
@ -256,7 +257,7 @@ public class EntityAccount extends EntityOrder implements Serializable {
account.provider = json.getString("provider"); account.provider = json.getString("provider");
account.user = json.getString("user"); account.user = json.getString("user");
account.password = json.getString("password"); account.password = json.getString("password");
account.certificate = json.optBoolean("certificate"); account.certificate_alias = json.getString("certificate_alias");
if (json.has("realm")) if (json.has("realm"))
account.realm = json.getString("realm"); account.realm = json.getString("realm");
if (json.has("fingerprint")) if (json.has("fingerprint"))

@ -78,7 +78,8 @@ public class EntityIdentity {
@NonNull @NonNull
public String password; public String password;
@NonNull @NonNull
public boolean certificate; public boolean certificate; // obsolete
public String certificate_alias;
public String realm; public String realm;
public String fingerprint; public String fingerprint;
@NonNull @NonNull
@ -175,7 +176,7 @@ public class EntityIdentity {
json.put("provider", provider); json.put("provider", provider);
json.put("user", user); json.put("user", user);
json.put("password", password); json.put("password", password);
json.put("certificate", certificate); json.put("certificate_alias", certificate_alias);
json.put("realm", realm); json.put("realm", realm);
json.put("fingerprint", fingerprint); json.put("fingerprint", fingerprint);
json.put("use_ip", use_ip); json.put("use_ip", use_ip);
@ -221,7 +222,7 @@ public class EntityIdentity {
identity.provider = json.getString("provider"); identity.provider = json.getString("provider");
identity.user = json.getString("user"); identity.user = json.getString("user");
identity.password = json.getString("password"); identity.password = json.getString("password");
identity.certificate = json.optBoolean("certificate"); identity.certificate_alias = json.getString("certificate_alias");
if (json.has("realm") && !json.isNull("realm")) if (json.has("realm") && !json.isNull("realm"))
identity.realm = json.getString("realm"); identity.realm = json.getString("realm");
if (json.has("fingerprint")) if (json.has("fingerprint"))

@ -91,7 +91,8 @@ public class FragmentAccount extends FragmentBase {
private EditText etUser; private EditText etUser;
private TextInputLayout tilPassword; private TextInputLayout tilPassword;
private TextView tvCharacters; private TextView tvCharacters;
private CheckBox cbCertificate; private Button btnCertificate;
private TextView tvCertificate;
private Button btnOAuth; private Button btnOAuth;
private TextView tvOAuthSupport; private TextView tvOAuthSupport;
private EditText etRealm; private EditText etRealm;
@ -152,6 +153,7 @@ public class FragmentAccount extends FragmentBase {
private long copy = -1; private long copy = -1;
private int auth = EmailService.AUTH_TYPE_PASSWORD; private int auth = EmailService.AUTH_TYPE_PASSWORD;
private String provider = null; private String provider = null;
private String certificate = null;
private boolean saving = false; private boolean saving = false;
private static final int REQUEST_COLOR = 1; private static final int REQUEST_COLOR = 1;
@ -200,7 +202,8 @@ public class FragmentAccount extends FragmentBase {
etUser = view.findViewById(R.id.etUser); etUser = view.findViewById(R.id.etUser);
tilPassword = view.findViewById(R.id.tilPassword); tilPassword = view.findViewById(R.id.tilPassword);
tvCharacters = view.findViewById(R.id.tvCharacters); tvCharacters = view.findViewById(R.id.tvCharacters);
cbCertificate = view.findViewById(R.id.cbCertificate); btnCertificate = view.findViewById(R.id.btnCertificate);
tvCertificate = view.findViewById(R.id.tvCertificate);
btnOAuth = view.findViewById(R.id.btnOAuth); btnOAuth = view.findViewById(R.id.btnOAuth);
tvOAuthSupport = view.findViewById(R.id.tvOAuthSupport); tvOAuthSupport = view.findViewById(R.id.tvOAuthSupport);
etRealm = view.findViewById(R.id.etRealm); etRealm = view.findViewById(R.id.etRealm);
@ -284,7 +287,8 @@ public class FragmentAccount extends FragmentBase {
etUser.setTag(null); etUser.setTag(null);
etUser.setText(null); etUser.setText(null);
tilPassword.getEditText().setText(null); tilPassword.getEditText().setText(null);
cbCertificate.setChecked(false); certificate = null;
tvCertificate.setText(R.string.title_optional);
btnOAuth.setEnabled(false); btnOAuth.setEnabled(false);
etRealm.setText(null); etRealm.setText(null);
cbTrust.setChecked(false); cbTrust.setChecked(false);
@ -339,6 +343,25 @@ public class FragmentAccount extends FragmentBase {
} }
}); });
btnCertificate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.selectKeyAlias(getActivity(), getViewLifecycleOwner(), null, new Helper.IKeyAlias() {
@Override
public void onSelected(String alias) {
certificate = alias;
tvCertificate.setText(alias);
}
@Override
public void onNothingSelected() {
certificate = null;
tvCertificate.setText(getString(R.string.title_optional));
}
});
}
});
btnOAuth.setOnClickListener(new View.OnClickListener() { btnOAuth.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -552,7 +575,7 @@ public class FragmentAccount extends FragmentBase {
args.putString("provider", provider); args.putString("provider", provider);
args.putString("user", etUser.getText().toString().trim()); args.putString("user", etUser.getText().toString().trim());
args.putString("password", tilPassword.getEditText().getText().toString()); args.putString("password", tilPassword.getEditText().getText().toString());
args.putBoolean("certificate", cbCertificate.isChecked()); args.putString("certificate", certificate);
args.putString("realm", etRealm.getText().toString()); args.putString("realm", etRealm.getText().toString());
args.putString("fingerprint", cbTrust.isChecked() ? (String) cbTrust.getTag() : null); args.putString("fingerprint", cbTrust.isChecked() ? (String) cbTrust.getTag() : null);
@ -591,7 +614,7 @@ public class FragmentAccount extends FragmentBase {
String provider = args.getString("provider"); String provider = args.getString("provider");
String user = args.getString("user"); String user = args.getString("user");
String password = args.getString("password"); String password = args.getString("password");
boolean certificate = args.getBoolean("certificate'"); String certificate = args.getString("certificate");
String realm = args.getString("realm"); String realm = args.getString("realm");
String fingerprint = args.getString("fingerprint"); String fingerprint = args.getString("fingerprint");
@ -606,7 +629,7 @@ public class FragmentAccount extends FragmentBase {
port = (starttls ? "143" : "993"); port = (starttls ? "143" : "993");
if (TextUtils.isEmpty(user)) if (TextUtils.isEmpty(user))
throw new IllegalArgumentException(context.getString(R.string.title_no_user)); throw new IllegalArgumentException(context.getString(R.string.title_no_user));
if (TextUtils.isEmpty(password) && !insecure && !certificate) if (TextUtils.isEmpty(password) && !insecure && certificate == null)
throw new IllegalArgumentException(context.getString(R.string.title_no_password)); throw new IllegalArgumentException(context.getString(R.string.title_no_password));
if (TextUtils.isEmpty(realm)) if (TextUtils.isEmpty(realm))
@ -769,7 +792,7 @@ public class FragmentAccount extends FragmentBase {
args.putString("provider", provider); args.putString("provider", provider);
args.putString("user", etUser.getText().toString().trim()); args.putString("user", etUser.getText().toString().trim());
args.putString("password", tilPassword.getEditText().getText().toString()); args.putString("password", tilPassword.getEditText().getText().toString());
args.putBoolean("certificate", cbCertificate.isChecked()); args.putString("certificate", certificate);
args.putString("realm", etRealm.getText().toString()); args.putString("realm", etRealm.getText().toString());
args.putString("fingerprint", cbTrust.isChecked() ? (String) cbTrust.getTag() : null); args.putString("fingerprint", cbTrust.isChecked() ? (String) cbTrust.getTag() : null);
@ -831,7 +854,7 @@ public class FragmentAccount extends FragmentBase {
String provider = args.getString("provider"); String provider = args.getString("provider");
String user = args.getString("user").trim(); String user = args.getString("user").trim();
String password = args.getString("password"); String password = args.getString("password");
boolean certificate = args.getBoolean("certificate"); String certificate = args.getString("certificate");
String realm = args.getString("realm"); String realm = args.getString("realm");
String fingerprint = args.getString("fingerprint"); String fingerprint = args.getString("fingerprint");
@ -872,7 +895,7 @@ public class FragmentAccount extends FragmentBase {
port = (starttls ? "143" : "993"); port = (starttls ? "143" : "993");
if (TextUtils.isEmpty(user) && !should) if (TextUtils.isEmpty(user) && !should)
throw new IllegalArgumentException(context.getString(R.string.title_no_user)); throw new IllegalArgumentException(context.getString(R.string.title_no_user));
if (synchronize && TextUtils.isEmpty(password) && !insecure && !certificate && !should) if (synchronize && TextUtils.isEmpty(password) && !insecure && certificate == null && !should)
throw new IllegalArgumentException(context.getString(R.string.title_no_password)); throw new IllegalArgumentException(context.getString(R.string.title_no_password));
if (TextUtils.isEmpty(interval)) if (TextUtils.isEmpty(interval))
interval = Integer.toString(EntityAccount.DEFAULT_KEEP_ALIVE_INTERVAL); interval = Integer.toString(EntityAccount.DEFAULT_KEEP_ALIVE_INTERVAL);
@ -909,7 +932,7 @@ public class FragmentAccount extends FragmentBase {
return true; return true;
if (!Objects.equals(account.password, password)) if (!Objects.equals(account.password, password))
return true; return true;
if (!Objects.equals(account.certificate, certificate)) if (!Objects.equals(account.certificate_alias, certificate))
return true; return true;
if (!Objects.equals(account.realm, realm)) if (!Objects.equals(account.realm, realm))
return true; return true;
@ -982,7 +1005,7 @@ public class FragmentAccount extends FragmentBase {
!account.port.equals(Integer.parseInt(port)) || !account.port.equals(Integer.parseInt(port)) ||
!account.user.equals(user) || !account.user.equals(user) ||
!account.password.equals(password) || !account.password.equals(password) ||
!account.certificate.equals(certificate) || !Objects.equals(account.certificate_alias, certificate) ||
!Objects.equals(realm, accountRealm) || !Objects.equals(realm, accountRealm) ||
!Objects.equals(account.fingerprint, fingerprint))); !Objects.equals(account.fingerprint, fingerprint)));
Log.i("Account check=" + check); Log.i("Account check=" + check);
@ -1045,7 +1068,7 @@ public class FragmentAccount extends FragmentBase {
account.auth_type = auth; account.auth_type = auth;
account.user = user; account.user = user;
account.password = password; account.password = password;
account.certificate = certificate; account.certificate_alias = certificate;
account.provider = provider; account.provider = provider;
account.realm = realm; account.realm = realm;
account.fingerprint = fingerprint; account.fingerprint = fingerprint;
@ -1382,7 +1405,8 @@ public class FragmentAccount extends FragmentBase {
etUser.setText(account == null ? null : account.user); etUser.setText(account == null ? null : account.user);
tilPassword.getEditText().setText(account == null ? null : account.password); tilPassword.getEditText().setText(account == null ? null : account.password);
cbCertificate.setChecked(account == null ? false : account.certificate); certificate = (account == null ? null : account.certificate_alias);
tvCertificate.setText(certificate == null ? getString(R.string.title_optional) : certificate);
etRealm.setText(account == null ? null : account.realm); etRealm.setText(account == null ? null : account.realm);
if (account == null || account.fingerprint == null) { if (account == null || account.fingerprint == null) {
@ -1447,7 +1471,7 @@ public class FragmentAccount extends FragmentBase {
if (auth != EmailService.AUTH_TYPE_PASSWORD) { if (auth != EmailService.AUTH_TYPE_PASSWORD) {
etUser.setEnabled(false); etUser.setEnabled(false);
tilPassword.setEnabled(false); tilPassword.setEnabled(false);
cbCertificate.setEnabled(false); btnCertificate.setEnabled(false);
} }
if (account == null || account.auth_type != EmailService.AUTH_TYPE_GMAIL) if (account == null || account.auth_type != EmailService.AUTH_TYPE_GMAIL)

@ -316,7 +316,7 @@ public class FragmentGmail extends FragmentBase {
provider.imap.host, provider.imap.port, provider.imap.host, provider.imap.port,
EmailService.AUTH_TYPE_GMAIL, null, EmailService.AUTH_TYPE_GMAIL, null,
user, password, user, password,
false, null); null, null);
folders = iservice.getFolders(); folders = iservice.getFolders();
@ -331,7 +331,7 @@ public class FragmentGmail extends FragmentBase {
provider.smtp.host, provider.smtp.port, provider.smtp.host, provider.smtp.port,
EmailService.AUTH_TYPE_GMAIL, null, EmailService.AUTH_TYPE_GMAIL, null,
user, password, user, password,
false, null); null, null);
} }
DB db = DB.getInstance(context); DB db = DB.getInstance(context);

@ -100,7 +100,8 @@ public class FragmentIdentity extends FragmentBase {
private EditText etUser; private EditText etUser;
private TextInputLayout tilPassword; private TextInputLayout tilPassword;
private TextView tvCharacters; private TextView tvCharacters;
private CheckBox cbCertificate; private Button btnCertificate;
private TextView tvCertificate;
private Button btnOAuth; private Button btnOAuth;
private EditText etRealm; private EditText etRealm;
private CheckBox cbUseIp; private CheckBox cbUseIp;
@ -132,6 +133,7 @@ public class FragmentIdentity extends FragmentBase {
private long account = -1; private long account = -1;
private int auth = EmailService.AUTH_TYPE_PASSWORD; private int auth = EmailService.AUTH_TYPE_PASSWORD;
private String provider = null; private String provider = null;
private String certificate = null;
private boolean saving = false; private boolean saving = false;
private static final int REQUEST_COLOR = 1; private static final int REQUEST_COLOR = 1;
@ -186,7 +188,8 @@ public class FragmentIdentity extends FragmentBase {
etUser = view.findViewById(R.id.etUser); etUser = view.findViewById(R.id.etUser);
tilPassword = view.findViewById(R.id.tilPassword); tilPassword = view.findViewById(R.id.tilPassword);
tvCharacters = view.findViewById(R.id.tvCharacters); tvCharacters = view.findViewById(R.id.tvCharacters);
cbCertificate = view.findViewById(R.id.cbCertificate); btnCertificate = view.findViewById(R.id.btnCertificate);
tvCertificate = view.findViewById(R.id.tvCertificate);
btnOAuth = view.findViewById(R.id.btnOAuth); btnOAuth = view.findViewById(R.id.btnOAuth);
etRealm = view.findViewById(R.id.etRealm); etRealm = view.findViewById(R.id.etRealm);
cbUseIp = view.findViewById(R.id.cbUseIp); cbUseIp = view.findViewById(R.id.cbUseIp);
@ -268,12 +271,13 @@ public class FragmentIdentity extends FragmentBase {
etEmail.setText(account.user); etEmail.setText(account.user);
etUser.setText(account.user); etUser.setText(account.user);
tilPassword.getEditText().setText(account.password); tilPassword.getEditText().setText(account.password);
cbCertificate.setChecked(account.certificate); certificate = account.certificate_alias;
tvCertificate.setText(certificate == null ? getString(R.string.title_optional) : certificate);
etRealm.setText(account.realm); etRealm.setText(account.realm);
etUser.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD); etUser.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
tilPassword.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD); tilPassword.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
cbCertificate.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD); btnCertificate.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
cbTrust.setChecked(false); cbTrust.setChecked(false);
} }
@ -406,7 +410,7 @@ public class FragmentIdentity extends FragmentBase {
etUser.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD); etUser.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
tilPassword.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD); tilPassword.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
cbCertificate.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD); btnCertificate.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
} }
@Override @Override
@ -428,6 +432,25 @@ public class FragmentIdentity extends FragmentBase {
} }
}); });
btnCertificate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.selectKeyAlias(getActivity(), getViewLifecycleOwner(), null, new Helper.IKeyAlias() {
@Override
public void onSelected(String alias) {
certificate = alias;
tvCertificate.setText(alias);
}
@Override
public void onNothingSelected() {
certificate = null;
tvCertificate.setText(R.string.title_optional);
}
});
}
});
btnOAuth.setOnClickListener(new View.OnClickListener() { btnOAuth.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -586,7 +609,7 @@ public class FragmentIdentity extends FragmentBase {
args.putString("provider", provider); args.putString("provider", provider);
args.putString("user", etUser.getText().toString().trim()); args.putString("user", etUser.getText().toString().trim());
args.putString("password", tilPassword.getEditText().getText().toString()); args.putString("password", tilPassword.getEditText().getText().toString());
args.putBoolean("certificate", cbCertificate.isChecked()); args.putString("certificate", certificate);
args.putString("realm", etRealm.getText().toString()); args.putString("realm", etRealm.getText().toString());
args.putString("fingerprint", cbTrust.isChecked() ? (String) cbTrust.getTag() : null); args.putString("fingerprint", cbTrust.isChecked() ? (String) cbTrust.getTag() : null);
args.putBoolean("use_ip", cbUseIp.isChecked()); args.putBoolean("use_ip", cbUseIp.isChecked());
@ -636,7 +659,7 @@ public class FragmentIdentity extends FragmentBase {
String provider = args.getString("provider"); String provider = args.getString("provider");
String user = args.getString("user").trim(); String user = args.getString("user").trim();
String password = args.getString("password"); String password = args.getString("password");
boolean certificate = args.getBoolean("certificate"); String certificate = args.getString("certificate");
String realm = args.getString("realm"); String realm = args.getString("realm");
String fingerprint = args.getString("fingerprint"); String fingerprint = args.getString("fingerprint");
boolean use_ip = args.getBoolean("use_ip"); boolean use_ip = args.getBoolean("use_ip");
@ -667,7 +690,7 @@ public class FragmentIdentity extends FragmentBase {
port = (starttls ? "587" : "465"); port = (starttls ? "587" : "465");
if (TextUtils.isEmpty(user) && !should) if (TextUtils.isEmpty(user) && !should)
throw new IllegalArgumentException(context.getString(R.string.title_no_user)); throw new IllegalArgumentException(context.getString(R.string.title_no_user));
if (synchronize && TextUtils.isEmpty(password) && !insecure && !certificate && !should) if (synchronize && TextUtils.isEmpty(password) && !insecure && certificate == null && !should)
throw new IllegalArgumentException(context.getString(R.string.title_no_password)); throw new IllegalArgumentException(context.getString(R.string.title_no_password));
if (!TextUtils.isEmpty(replyto) && !should) { if (!TextUtils.isEmpty(replyto) && !should) {
@ -739,7 +762,7 @@ public class FragmentIdentity extends FragmentBase {
return true; return true;
if (!Objects.equals(identity.password, password)) if (!Objects.equals(identity.password, password))
return true; return true;
if (!Objects.equals(identity.certificate, certificate)) if (!Objects.equals(identity.certificate_alias, certificate))
return true; return true;
if (!Objects.equals(identity.realm, realm)) if (!Objects.equals(identity.realm, realm))
return true; return true;
@ -770,7 +793,7 @@ public class FragmentIdentity extends FragmentBase {
!identity.insecure.equals(insecure) || !identity.insecure.equals(insecure) ||
!host.equals(identity.host) || Integer.parseInt(port) != identity.port || !host.equals(identity.host) || Integer.parseInt(port) != identity.port ||
!user.equals(identity.user) || !password.equals(identity.password) || !user.equals(identity.user) || !password.equals(identity.password) ||
!Objects.equals(identity.certificate, certificate) || !Objects.equals(identity.certificate_alias, certificate) ||
!Objects.equals(realm, identityRealm) || !Objects.equals(realm, identityRealm) ||
!Objects.equals(identity.fingerprint, fingerprint) || !Objects.equals(identity.fingerprint, fingerprint) ||
use_ip != identity.use_ip)); use_ip != identity.use_ip));
@ -823,7 +846,7 @@ public class FragmentIdentity extends FragmentBase {
identity.auth_type = auth; identity.auth_type = auth;
identity.user = user; identity.user = user;
identity.password = password; identity.password = password;
identity.certificate = certificate; identity.certificate_alias = certificate;
identity.provider = provider; identity.provider = provider;
identity.realm = realm; identity.realm = realm;
identity.fingerprint = fingerprint; identity.fingerprint = fingerprint;
@ -1019,7 +1042,8 @@ public class FragmentIdentity extends FragmentBase {
etPort.setText(identity == null ? null : Long.toString(identity.port)); etPort.setText(identity == null ? null : Long.toString(identity.port));
etUser.setText(identity == null ? null : identity.user); etUser.setText(identity == null ? null : identity.user);
tilPassword.getEditText().setText(identity == null ? null : identity.password); tilPassword.getEditText().setText(identity == null ? null : identity.password);
cbCertificate.setChecked(identity != null && identity.certificate); certificate = (identity == null ? null : identity.certificate_alias);
tvCertificate.setText(certificate == null ? getString(R.string.title_optional) : certificate);
etRealm.setText(identity == null ? null : identity.realm); etRealm.setText(identity == null ? null : identity.realm);
if (identity == null || identity.fingerprint == null) { if (identity == null || identity.fingerprint == null) {
@ -1075,7 +1099,7 @@ public class FragmentIdentity extends FragmentBase {
if (auth != EmailService.AUTH_TYPE_PASSWORD) { if (auth != EmailService.AUTH_TYPE_PASSWORD) {
etUser.setEnabled(false); etUser.setEnabled(false);
tilPassword.setEnabled(false); tilPassword.setEnabled(false);
cbCertificate.setEnabled(false); btnCertificate.setEnabled(false);
} }
if (identity == null || identity.auth_type != EmailService.AUTH_TYPE_GMAIL) if (identity == null || identity.auth_type != EmailService.AUTH_TYPE_GMAIL)

@ -475,7 +475,7 @@ public class FragmentOAuth extends FragmentBase {
provider.imap.host, provider.imap.port, provider.imap.host, provider.imap.port,
EmailService.AUTH_TYPE_OAUTH, provider.id, EmailService.AUTH_TYPE_OAUTH, provider.id,
primaryEmail, state, primaryEmail, state,
false, null); null, null);
folders = iservice.getFolders(); folders = iservice.getFolders();
@ -491,7 +491,7 @@ public class FragmentOAuth extends FragmentBase {
provider.smtp.host, provider.smtp.port, provider.smtp.host, provider.smtp.port,
EmailService.AUTH_TYPE_OAUTH, provider.id, EmailService.AUTH_TYPE_OAUTH, provider.id,
primaryEmail, state, primaryEmail, state,
false, null); null, null);
} }
Log.i("OAuth passed provider=" + provider.id); Log.i("OAuth passed provider=" + provider.id);

@ -329,7 +329,7 @@ public class FragmentPop extends FragmentBase {
host, Integer.parseInt(port), host, Integer.parseInt(port),
EmailService.AUTH_TYPE_PASSWORD, null, EmailService.AUTH_TYPE_PASSWORD, null,
user, password, user, password,
false, null); null, null);
} }
} }

@ -285,7 +285,7 @@ public class FragmentQuickSetup extends FragmentBase {
provider.imap.host, provider.imap.port, provider.imap.host, provider.imap.port,
EmailService.AUTH_TYPE_PASSWORD, null, EmailService.AUTH_TYPE_PASSWORD, null,
user, password, user, password,
false, null); null, null);
} catch (AuthenticationFailedException ex) { } catch (AuthenticationFailedException ex) {
if (!user.equals(username)) { if (!user.equals(username)) {
Log.w(ex); Log.w(ex);
@ -295,7 +295,7 @@ public class FragmentQuickSetup extends FragmentBase {
provider.imap.host, provider.imap.port, provider.imap.host, provider.imap.port,
EmailService.AUTH_TYPE_PASSWORD, null, EmailService.AUTH_TYPE_PASSWORD, null,
user, password, user, password,
false, null); null, null);
} else } else
throw ex; throw ex;
} }
@ -314,7 +314,7 @@ public class FragmentQuickSetup extends FragmentBase {
provider.smtp.host, provider.smtp.port, provider.smtp.host, provider.smtp.port,
EmailService.AUTH_TYPE_PASSWORD, null, EmailService.AUTH_TYPE_PASSWORD, null,
user, password, user, password,
false, null); null, null);
} }
if (check) if (check)

@ -36,6 +36,7 @@ public class TupleAccountState extends EntityAccount {
this.port.equals(other.port) && this.port.equals(other.port) &&
this.user.equals(other.user) && this.user.equals(other.user) &&
this.password.equals(other.password) && this.password.equals(other.password) &&
Objects.equals(this.certificate_alias, other.certificate_alias) &&
Objects.equals(this.realm, other.realm) && Objects.equals(this.realm, other.realm) &&
Objects.equals(this.fingerprint, other.fingerprint) && Objects.equals(this.fingerprint, other.fingerprint) &&
this.notify.equals(other.notify) && this.notify.equals(other.notify) &&

@ -255,15 +255,31 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tilPassword" /> app:layout_constraintTop_toBottomOf="@id/tilPassword" />
<CheckBox <Button
android:id="@+id/cbCertificate" android:id="@+id/btnCertificate"
style="@style/buttonStyleSmall"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:text="@string/title_use_certificate" android:minWidth="0dp"
android:minHeight="0dp"
android:tag="disable"
android:text="@string/title_client_certificate"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvCharacters" /> app:layout_constraintTop_toBottomOf="@id/tvCharacters" />
<TextView
android:id="@+id/tvCertificate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@string/title_optional"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textStyle="italic"
app:layout_constraintBottom_toBottomOf="@+id/btnCertificate"
app:layout_constraintStart_toEndOf="@id/btnCertificate"
app:layout_constraintTop_toTopOf="@id/btnCertificate" />
<Button <Button
android:id="@+id/btnOAuth" android:id="@+id/btnOAuth"
style="@style/buttonStyleSmall" style="@style/buttonStyleSmall"
@ -275,7 +291,7 @@
android:tag="disable" android:tag="disable"
android:text="@string/title_setup_authentication" android:text="@string/title_setup_authentication"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbCertificate" /> app:layout_constraintTop_toBottomOf="@id/btnCertificate" />
<TextView <TextView
android:id="@+id/tvOAuthSupport" android:id="@+id/tvOAuthSupport"
@ -915,7 +931,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
app:constraint_referenced_ids=" app:constraint_referenced_ids="
tvUser,etUser,tvPassword,tilPassword,cbCertificate,btnOAuth,tvOAuthSupport,tvRealm,etRealm, tvUser,etUser,tvPassword,tilPassword,btnCertificate,tvCertificate,btnOAuth,tvOAuthSupport,tvRealm,etRealm,
tvName,tvNameRemark,etName, tvName,tvNameRemark,etName,
tvColor,btnColor,tvColorHint,tvColorPro" /> tvColor,btnColor,tvColorHint,tvColorPro" />

@ -442,15 +442,31 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tilPassword" /> app:layout_constraintTop_toBottomOf="@id/tilPassword" />
<CheckBox <Button
android:id="@+id/cbCertificate" android:id="@+id/btnCertificate"
style="@style/buttonStyleSmall"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:text="@string/title_use_certificate" android:minWidth="0dp"
android:minHeight="0dp"
android:tag="disable"
android:text="@string/title_client_certificate"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvCharacters" /> app:layout_constraintTop_toBottomOf="@id/tvCharacters" />
<TextView
android:id="@+id/tvCertificate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@string/title_optional"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textStyle="italic"
app:layout_constraintBottom_toBottomOf="@+id/btnCertificate"
app:layout_constraintStart_toEndOf="@id/btnCertificate"
app:layout_constraintTop_toTopOf="@id/btnCertificate" />
<Button <Button
android:id="@+id/btnOAuth" android:id="@+id/btnOAuth"
style="@style/buttonStyleSmall" style="@style/buttonStyleSmall"
@ -462,7 +478,7 @@
android:tag="disable" android:tag="disable"
android:text="@string/title_setup_authentication" android:text="@string/title_setup_authentication"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbCertificate" /> app:layout_constraintTop_toBottomOf="@id/btnCertificate" />
<TextView <TextView
android:id="@+id/tvRealm" android:id="@+id/tvRealm"
@ -723,7 +739,7 @@
tvProvider,spProvider, tvProvider,spProvider,
tvDomain,tvDomainHint,etDomain,btnAutoConfig, tvDomain,tvDomainHint,etDomain,btnAutoConfig,
tvSmtp,tvHost,etHost,rgEncryption,cbInsecure,tvInsecureRemark,tvPort,etPort, tvSmtp,tvHost,etHost,rgEncryption,cbInsecure,tvInsecureRemark,tvPort,etPort,
tvUser,etUser,tvPassword,tilPassword,cbCertificate,btnOAuth, tvUser,etUser,tvPassword,tilPassword,btnCertificate,tvCertificate,btnOAuth,
tvRealm,etRealm, tvRealm,etRealm,
cbUseIp,tvUseIpHint, cbUseIp,tvUseIpHint,
cbSynchronize,cbPrimary, cbSynchronize,cbPrimary,

@ -517,7 +517,7 @@
<string name="title_port">Port number</string> <string name="title_port">Port number</string>
<string name="title_user">User name</string> <string name="title_user">User name</string>
<string name="title_password">Password</string> <string name="title_password">Password</string>
<string name="title_use_certificate">Use client certificate</string> <string name="title_client_certificate">Client certificate</string>
<string name="title_realm">Realm</string> <string name="title_realm">Realm</string>
<string name="title_use_ip">Use local IP address instead of host name</string> <string name="title_use_ip">Use local IP address instead of host name</string>
<string name="title_primary_account">Primary (default account)</string> <string name="title_primary_account">Primary (default account)</string>

Loading…
Cancel
Save