Added encryption none

This commit is contained in:
M66B 2020-08-23 09:28:10 +02:00
parent 38c389989c
commit bcd668a622
18 changed files with 247 additions and 161 deletions

View File

@ -425,7 +425,7 @@ public class ActivityEML extends ActivityBase {
MimeMessage imessage = new MimeMessage(isession, is);
try (EmailService iservice = new EmailService(
context, account.getProtocol(), account.realm, account.insecure, true)) {
context, account.getProtocol(), account.realm, account.encryption, account.insecure, true)) {
iservice.setPartialFetch(account.partial_fetch);
iservice.setIgnoreBodyStructureSize(account.ignore_size);
iservice.connect(account);

View File

@ -330,7 +330,7 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
EntityLog.log(context, "Boundary server connecting account=" + account.name);
state.iservice = new EmailService(
context, account.getProtocol(), account.realm, account.insecure,
context, account.getProtocol(), account.realm, account.encryption, account.insecure,
EmailService.PURPOSE_SEARCH, debug || BuildConfig.DEBUG);
state.iservice.setPartialFetch(account.partial_fetch);
state.iservice.setIgnoreBodyStructureSize(account.ignore_size);

View File

@ -107,6 +107,10 @@ public class EmailService implements AutoCloseable {
static final int PURPOSE_USE = 2;
static final int PURPOSE_SEARCH = 3;
static final int ENCRYPTION_SSL = 0;
static final int ENCRYPTION_STARTTLS = 1;
static final int ENCRYPTION_NONE = 2;
final static int DEFAULT_CONNECT_TIMEOUT = 20; // seconds
private final static int SEARCH_TIMEOUT = 90 * 1000; // milliseconds
@ -131,11 +135,11 @@ public class EmailService implements AutoCloseable {
// Prevent instantiation
}
EmailService(Context context, String protocol, String realm, boolean insecure, boolean debug) throws NoSuchProviderException {
this(context, protocol, realm, insecure, PURPOSE_USE, debug);
EmailService(Context context, String protocol, String realm, int encryption, boolean insecure, boolean debug) throws NoSuchProviderException {
this(context, protocol, realm, encryption, insecure, PURPOSE_USE, debug);
}
EmailService(Context context, String protocol, String realm, boolean insecure, int purpose, boolean debug) throws NoSuchProviderException {
EmailService(Context context, String protocol, String realm, int encryption, boolean insecure, int purpose, boolean debug) throws NoSuchProviderException {
this.context = context.getApplicationContext();
this.protocol = protocol;
this.insecure = insecure;
@ -183,19 +187,21 @@ public class EmailService implements AutoCloseable {
if (debug && BuildConfig.DEBUG)
properties.put("mail.debug.auth", "true");
boolean starttls = (encryption == ENCRYPTION_STARTTLS);
if ("pop3".equals(protocol) || "pop3s".equals(protocol)) {
// https://javaee.github.io/javamail/docs/api/com/sun/mail/pop3/package-summary.html#properties
properties.put("mail.pop3s.starttls.enable", "false");
properties.put("mail.pop3.starttls.enable", "true");
properties.put("mail.pop3.starttls.required", Boolean.toString(!insecure));
properties.put("mail.pop3.starttls.enable", Boolean.toString(starttls));
properties.put("mail.pop3.starttls.required", Boolean.toString(starttls && !insecure));
} else if ("imap".equals(protocol) || "imaps".equals(protocol) || "gimaps".equals(protocol)) {
// https://javaee.github.io/javamail/docs/api/com/sun/mail/imap/package-summary.html#properties
properties.put("mail.imaps.starttls.enable", "false");
properties.put("mail.imap.starttls.enable", "true");
properties.put("mail.imap.starttls.required", Boolean.toString(!insecure));
properties.put("mail.imap.starttls.enable", Boolean.toString(starttls));
properties.put("mail.imap.starttls.required", Boolean.toString(starttls && !insecure));
properties.put("mail." + protocol + ".separatestoreconnection", "true");
properties.put("mail." + protocol + ".connectionpool.debug", "true");
@ -220,8 +226,8 @@ public class EmailService implements AutoCloseable {
// https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html#properties
properties.put("mail.smtps.starttls.enable", "false");
properties.put("mail.smtp.starttls.enable", "true");
properties.put("mail.smtp.starttls.required", Boolean.toString(!insecure));
properties.put("mail.smtp.starttls.enable", Boolean.toString(starttls));
properties.put("mail.smtp.starttls.required", Boolean.toString(starttls && !insecure));
properties.put("mail." + protocol + ".auth", "true");
@ -510,18 +516,7 @@ public class EmailService implements AutoCloseable {
iservice = isession.getStore(protocol);
if (listener != null)
((IMAPStore) iservice).addStoreListener(listener);
try {
iservice.connect(address.getHostAddress(), port, user, password);
} catch (MessagingException ex) {
if (startTlsFailure(ex)) {
// Workaround servers with non working STARTTLS capability
Log.i("Disabling STARTTLS");
properties.put("mail.imap.starttls.enable", "false");
iservice = isession.getStore(protocol);
iservice.connect(address.getHostAddress(), port, user, password);
} else
throw ex;
}
iservice.connect(address.getHostAddress(), port, user, password);
// https://www.ietf.org/rfc/rfc2971.txt
IMAPStore istore = (IMAPStore) getStore();
@ -562,13 +557,7 @@ public class EmailService implements AutoCloseable {
try {
iservice.connect(address.getHostAddress(), port, user, password);
} catch (MessagingException ex) {
if (startTlsFailure(ex)) {
// Workaround servers with non working STARTTLS capability
Log.i("Disabling STARTTLS");
properties.put("mail.smtp.starttls.enable", "false");
iservice = isession.getStore(protocol);
iservice.connect(address.getHostAddress(), port, user, password);
} else if (ehlo == null && ConnectionHelper.isSyntacticallyInvalid(ex)) {
if (ehlo == null && ConnectionHelper.isSyntacticallyInvalid(ex)) {
properties.put("mail." + protocol + ".localhost", useip ? hdomain : haddr);
Log.i("Fallback localhost=" + properties.getProperty("mail." + protocol + ".localhost"));
try {
@ -585,20 +574,6 @@ public class EmailService implements AutoCloseable {
throw new NoSuchProviderException(protocol);
}
private boolean startTlsFailure(MessagingException ex) {
if (!"STARTTLS failure".equals(ex.getMessage()))
return false;
Throwable cause = ex.getCause();
while (cause != null && cause.getCause() != null)
cause = cause.getCause();
if (cause == null)
return false;
return "Unable to parse TLS packet header".equals(cause.getMessage());
}
private static class ErrorHolder {
AuthorizationException error;
}

View File

@ -66,7 +66,8 @@ public class EntityAccount extends EntityOrder implements Serializable {
@NonNull
public String host; // POP3/IMAP
@NonNull
public Boolean starttls;
@ColumnInfo(name = "starttls")
public Integer encryption;
@NonNull
public Boolean insecure = false;
@NonNull
@ -153,9 +154,9 @@ public class EntityAccount extends EntityOrder implements Serializable {
if (isGmail())
return "gimaps";
else
return "imap" + (starttls ? "" : "s");
return "imap" + (encryption == EmailService.ENCRYPTION_SSL ? "s" : "");
case TYPE_POP:
return "pop3" + (starttls ? "" : "s");
return "pop3" + (encryption == EmailService.ENCRYPTION_SSL ? "s" : "");
default:
throw new IllegalArgumentException("Unknown protocol=" + protocol);
}
@ -203,7 +204,7 @@ public class EntityAccount extends EntityOrder implements Serializable {
json.put("order", order);
json.put("protocol", protocol);
json.put("host", host);
json.put("starttls", starttls);
json.put("encryption", encryption);
json.put("insecure", insecure);
json.put("port", port);
json.put("auth_type", auth_type);
@ -264,7 +265,11 @@ public class EntityAccount extends EntityOrder implements Serializable {
account.protocol = (json.getBoolean("pop") ? TYPE_POP : TYPE_IMAP);
account.host = json.getString("host");
account.starttls = (json.has("starttls") && json.getBoolean("starttls"));
if (json.has("starttls"))
account.encryption = (json.getBoolean("starttls")
? EmailService.ENCRYPTION_STARTTLS : EmailService.ENCRYPTION_SSL);
else
account.encryption = json.getInt("encryption");
account.insecure = (json.has("insecure") && json.getBoolean("insecure"));
account.port = json.getInt("port");
account.auth_type = json.getInt("auth_type");
@ -330,7 +335,7 @@ public class EntityAccount extends EntityOrder implements Serializable {
return (Objects.equals(this.order, other.order) &&
this.protocol.equals(other.protocol) &&
this.host.equals(other.host) &&
this.starttls == other.starttls &&
this.encryption.equals(other.encryption) &&
this.insecure == other.insecure &&
this.port.equals(other.port) &&
this.auth_type.equals(other.auth_type) &&

View File

@ -22,6 +22,7 @@ package eu.faircode.email;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
@ -65,7 +66,8 @@ public class EntityIdentity {
@NonNull
public String host; // SMTP
@NonNull
public Boolean starttls;
@ColumnInfo(name = "starttls")
public Integer encryption;
@NonNull
public Boolean insecure = false;
@NonNull
@ -119,7 +121,7 @@ public class EntityIdentity {
public Long max_size;
String getProtocol() {
return (starttls ? "smtp" : "smtps");
return (encrypt == EmailService.ENCRYPTION_SSL ? "smtps" : "smtp");
}
boolean sameAddress(Address address) {
@ -178,7 +180,7 @@ public class EntityIdentity {
// not account
json.put("host", host);
json.put("starttls", starttls);
json.put("encryption", encryption);
json.put("insecure", insecure);
json.put("port", port);
json.put("auth_type", auth_type);
@ -226,7 +228,11 @@ public class EntityIdentity {
identity.signature = json.getString("signature");
identity.host = json.getString("host");
identity.starttls = json.getBoolean("starttls");
if (json.has("starttls"))
identity.encryption = (json.getBoolean("starttls")
? EmailService.ENCRYPTION_STARTTLS : EmailService.ENCRYPTION_SSL);
else
identity.encryption = json.getInt("encryption");
identity.insecure = (json.has("insecure") && json.getBoolean("insecure"));
identity.port = json.getInt("port");
identity.auth_type = json.getInt("auth_type");
@ -275,7 +281,7 @@ public class EntityIdentity {
Objects.equals(this.color, other.color) &&
Objects.equals(this.signature, other.signature) &&
this.host.equals(other.host) &&
this.starttls.equals(other.starttls) &&
this.encryption.equals(other.encryption) &&
this.insecure.equals(other.insecure) &&
this.port.equals(other.port) &&
this.auth_type.equals(other.auth_type) &&

View File

@ -73,8 +73,6 @@ import java.util.Objects;
import javax.mail.Folder;
import static android.app.Activity.RESULT_OK;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static com.google.android.material.textfield.TextInputLayout.END_ICON_NONE;
import static com.google.android.material.textfield.TextInputLayout.END_ICON_PASSWORD_TOGGLE;
@ -265,7 +263,7 @@ public class FragmentAccount extends FragmentBase {
EmailProvider provider = (EmailProvider) adapterView.getSelectedItem();
tvGmailHint.setVisibility(
auth == EmailService.AUTH_TYPE_PASSWORD && "gmail".equals(provider.id)
? VISIBLE : GONE);
? View.VISIBLE : View.GONE);
grpServer.setVisibility(position > 0 ? View.VISIBLE : View.GONE);
grpAuthorize.setVisibility(position > 0 ? View.VISIBLE : View.GONE);
@ -319,18 +317,7 @@ public class FragmentAccount extends FragmentBase {
rgEncryption.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int id) {
etPort.setHint(id == R.id.radio_starttls ? "143" : "993");
}
});
cbInsecure.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Object tag = cbInsecure.getTag();
if (tag != null && tag.equals(isChecked))
return;
if (isChecked)
rgEncryption.check(R.id.radio_starttls);
etPort.setHint(id == R.id.radio_ssl ? "993" : "143");
}
});
@ -522,7 +509,7 @@ public class FragmentAccount extends FragmentBase {
// Initialize
Helper.setViewsEnabled(view, false);
tvGmailHint.setVisibility(GONE);
tvGmailHint.setVisibility(View.GONE);
btnAutoConfig.setEnabled(false);
pbAutoConfig.setVisibility(View.GONE);
@ -556,7 +543,7 @@ public class FragmentAccount extends FragmentBase {
grpFolders.setVisibility(View.GONE);
grpError.setVisibility(View.GONE);
pbWait.setVisibility(VISIBLE);
pbWait.setVisibility(View.VISIBLE);
return view;
}
@ -605,10 +592,18 @@ public class FragmentAccount extends FragmentBase {
}
private void onCheck() {
int encryption;
if (rgEncryption.getCheckedRadioButtonId() == R.id.radio_starttls)
encryption = EmailService.ENCRYPTION_STARTTLS;
else if (rgEncryption.getCheckedRadioButtonId() == R.id.radio_none)
encryption = EmailService.ENCRYPTION_NONE;
else
encryption = EmailService.ENCRYPTION_SSL;
Bundle args = new Bundle();
args.putLong("id", id);
args.putString("host", etHost.getText().toString().trim());
args.putBoolean("starttls", rgEncryption.getCheckedRadioButtonId() == R.id.radio_starttls);
args.putInt("encryption", encryption);
args.putBoolean("insecure", cbInsecure.isChecked());
args.putString("port", etPort.getText().toString());
args.putInt("auth", auth);
@ -647,7 +642,7 @@ public class FragmentAccount extends FragmentBase {
protected CheckResult onExecute(Context context, Bundle args) throws Throwable {
long id = args.getLong("id");
String host = args.getString("host");
boolean starttls = args.getBoolean("starttls");
int encryption = args.getInt("encryption");
boolean insecure = args.getBoolean("insecure");
String port = args.getString("port");
int auth = args.getInt("auth");
@ -666,7 +661,7 @@ public class FragmentAccount extends FragmentBase {
if (TextUtils.isEmpty(host))
throw new IllegalArgumentException(context.getString(R.string.title_no_host));
if (TextUtils.isEmpty(port))
port = (starttls ? "143" : "993");
port = (encryption == EmailService.ENCRYPTION_SSL ? "993" : "143");
if (TextUtils.isEmpty(user))
throw new IllegalArgumentException(context.getString(R.string.title_no_user));
if (TextUtils.isEmpty(password) && !insecure && certificate == null)
@ -682,9 +677,10 @@ public class FragmentAccount extends FragmentBase {
result.folders = new ArrayList<>();
// Check IMAP server / get folders
String protocol = "imap" + (starttls ? "" : "s");
String protocol = "imap" + (encryption == EmailService.ENCRYPTION_SSL ? "s" : "");
try (EmailService iservice = new EmailService(
context, protocol, realm, insecure, EmailService.PURPOSE_CHECK, true)) {
context, protocol, realm, encryption, insecure,
EmailService.PURPOSE_CHECK, true)) {
iservice.connect(
host, Integer.parseInt(port),
auth, provider,
@ -809,8 +805,16 @@ public class FragmentAccount extends FragmentBase {
Bundle args = new Bundle();
args.putLong("id", id);
int encryption;
if (rgEncryption.getCheckedRadioButtonId() == R.id.radio_starttls)
encryption = EmailService.ENCRYPTION_STARTTLS;
else if (rgEncryption.getCheckedRadioButtonId() == R.id.radio_none)
encryption = EmailService.ENCRYPTION_NONE;
else
encryption = EmailService.ENCRYPTION_SSL;
args.putString("host", etHost.getText().toString().trim());
args.putBoolean("starttls", rgEncryption.getCheckedRadioButtonId() == R.id.radio_starttls);
args.putInt("encryption", encryption);
args.putBoolean("insecure", cbInsecure.isChecked());
args.putString("port", etPort.getText().toString());
args.putInt("auth", auth);
@ -873,7 +877,7 @@ public class FragmentAccount extends FragmentBase {
long id = args.getLong("id");
String host = args.getString("host");
boolean starttls = args.getBoolean("starttls");
int encryption = args.getInt("encryption");
boolean insecure = args.getBoolean("insecure");
String port = args.getString("port");
int auth = args.getInt("auth");
@ -919,7 +923,7 @@ public class FragmentAccount extends FragmentBase {
if (TextUtils.isEmpty(host) && !should)
throw new IllegalArgumentException(context.getString(R.string.title_no_host));
if (TextUtils.isEmpty(port))
port = (starttls ? "143" : "993");
port = (encryption == EmailService.ENCRYPTION_SSL ? "993" : "143");
if (TextUtils.isEmpty(user) && !should)
throw new IllegalArgumentException(context.getString(R.string.title_no_user));
if (synchronize && TextUtils.isEmpty(password) && !insecure && certificate == null && !should)
@ -947,7 +951,7 @@ public class FragmentAccount extends FragmentBase {
if (!Objects.equals(account.host, host))
return true;
if (!Objects.equals(account.starttls, starttls))
if (!Objects.equals(account.encryption, encryption))
return true;
if (!Objects.equals(account.insecure, insecure))
return true;
@ -1029,7 +1033,7 @@ public class FragmentAccount extends FragmentBase {
!account.synchronize ||
account.error != null ||
!account.host.equals(host) ||
!account.starttls.equals(starttls) ||
!account.encryption.equals(encryption) ||
!account.insecure.equals(insecure) ||
!account.port.equals(Integer.parseInt(port)) ||
!account.user.equals(user) ||
@ -1047,9 +1051,10 @@ public class FragmentAccount extends FragmentBase {
// Check IMAP server
EntityFolder inbox = null;
if (check) {
String protocol = "imap" + (starttls ? "" : "s");
String protocol = "imap" + (encryption == EmailService.ENCRYPTION_SSL ? "s" : "");
try (EmailService iservice = new EmailService(
context, protocol, realm, insecure, EmailService.PURPOSE_CHECK, true)) {
context, protocol, realm, encryption, insecure,
EmailService.PURPOSE_CHECK, true)) {
iservice.connect(
host, Integer.parseInt(port),
auth, provider,
@ -1092,7 +1097,7 @@ public class FragmentAccount extends FragmentBase {
account = new EntityAccount();
account.host = host;
account.starttls = starttls;
account.encryption = encryption;
account.insecure = insecure;
account.port = Integer.parseInt(port);
account.auth_type = auth;
@ -1446,7 +1451,7 @@ public class FragmentAccount extends FragmentBase {
EmailProvider provider = providers.get(pos);
if (provider.imap.host.equals(account.host) &&
provider.imap.port == account.port &&
provider.imap.starttls == account.starttls) {
provider.imap.starttls == (account.encryption == EmailService.ENCRYPTION_STARTTLS)) {
found = true;
spProvider.setTag(pos);
spProvider.setSelection(pos);
@ -1461,8 +1466,13 @@ public class FragmentAccount extends FragmentBase {
etPort.setText(Long.toString(account.port));
}
rgEncryption.check(account != null && account.starttls ? R.id.radio_starttls : R.id.radio_ssl);
cbInsecure.setTag(account == null ? false : account.insecure);
if (account != null && account.encryption == EmailService.ENCRYPTION_STARTTLS)
rgEncryption.check(R.id.radio_starttls);
else if (account != null && account.encryption == EmailService.ENCRYPTION_NONE)
rgEncryption.check(R.id.radio_none);
else
rgEncryption.check(R.id.radio_ssl);
cbInsecure.setChecked(account == null ? false : account.insecure);
etUser.setText(account == null ? null : account.user);

View File

@ -363,9 +363,11 @@ public class FragmentGmail extends FragmentBase {
List<EntityFolder> folders;
String aprotocol = provider.imap.starttls ? "imap" : "imaps";
String aprotocol = (provider.imap.starttls ? "imap" : "imaps");
int aencryption = (provider.imap.starttls ? EmailService.ENCRYPTION_STARTTLS : EmailService.ENCRYPTION_SSL);
try (EmailService iservice = new EmailService(
context, aprotocol, null, false, EmailService.PURPOSE_CHECK, true)) {
context, aprotocol, null, aencryption, false,
EmailService.PURPOSE_CHECK, true)) {
iservice.connect(
provider.imap.host, provider.imap.port,
EmailService.AUTH_TYPE_GMAIL, null,
@ -376,9 +378,11 @@ public class FragmentGmail extends FragmentBase {
}
Long max_size;
String iprotocol = provider.smtp.starttls ? "smtp" : "smtps";
String iprotocol = (provider.smtp.starttls ? "smtp" : "smtps");
int iencryption = (provider.smtp.starttls ? EmailService.ENCRYPTION_STARTTLS : EmailService.ENCRYPTION_SSL);
try (EmailService iservice = new EmailService(
context, iprotocol, null, false, EmailService.PURPOSE_CHECK, true)) {
context, iprotocol, null, iencryption, false,
EmailService.PURPOSE_CHECK, true)) {
iservice.connect(
provider.smtp.host, provider.smtp.port,
EmailService.AUTH_TYPE_GMAIL, null,
@ -397,7 +401,7 @@ public class FragmentGmail extends FragmentBase {
EntityAccount account = new EntityAccount();
account.host = provider.imap.host;
account.starttls = provider.imap.starttls;
account.encryption = aencryption;
account.port = provider.imap.port;
account.auth_type = EmailService.AUTH_TYPE_GMAIL;
account.user = user;
@ -446,7 +450,7 @@ public class FragmentGmail extends FragmentBase {
identity.account = account.id;
identity.host = provider.smtp.host;
identity.starttls = provider.smtp.starttls;
identity.encryption = iencryption;
identity.port = provider.smtp.port;
identity.auth_type = EmailService.AUTH_TYPE_GMAIL;
identity.user = user;

View File

@ -253,7 +253,7 @@ public class FragmentIdentity extends FragmentBase {
EmailProvider provider = (EmailProvider) spProvider.getItemAtPosition(pos);
if (provider.imap.host.equals(account.host) &&
provider.imap.port == account.port &&
provider.imap.starttls == account.starttls) {
provider.imap.starttls == (account.encryption == EmailService.ENCRYPTION_STARTTLS)) {
found = true;
spProvider.setSelection(pos);
@ -418,17 +418,6 @@ public class FragmentIdentity extends FragmentBase {
}
});
cbInsecure.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Object tag = cbInsecure.getTag();
if (tag != null && tag.equals(isChecked))
return;
if (isChecked)
rgEncryption.check(R.id.radio_starttls);
}
});
btnCertificate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -590,6 +579,14 @@ public class FragmentIdentity extends FragmentBase {
name = hint.toString();
}
int encryption;
if (rgEncryption.getCheckedRadioButtonId() == R.id.radio_starttls)
encryption = EmailService.ENCRYPTION_STARTTLS;
else if (rgEncryption.getCheckedRadioButtonId() == R.id.radio_none)
encryption = EmailService.ENCRYPTION_NONE;
else
encryption = EmailService.ENCRYPTION_SSL;
Bundle args = new Bundle();
args.putLong("id", id);
args.putString("name", name);
@ -605,7 +602,7 @@ public class FragmentIdentity extends FragmentBase {
args.putString("max_size", etMaxSize.getText().toString());
args.putLong("account", account == null ? -1 : account.id);
args.putString("host", etHost.getText().toString().trim());
args.putBoolean("starttls", rgEncryption.getCheckedRadioButtonId() == R.id.radio_starttls);
args.putInt("encryption", encryption);
args.putBoolean("insecure", cbInsecure.isChecked());
args.putString("port", etPort.getText().toString());
args.putInt("auth", auth);
@ -657,7 +654,7 @@ public class FragmentIdentity extends FragmentBase {
String signature = args.getString("signature");
String host = args.getString("host");
boolean starttls = args.getBoolean("starttls");
int encryption = args.getInt("encryption");
boolean insecure = args.getBoolean("insecure");
String port = args.getString("port");
int auth = args.getInt("auth");
@ -697,7 +694,7 @@ public class FragmentIdentity extends FragmentBase {
if (TextUtils.isEmpty(host) && !should)
throw new IllegalArgumentException(context.getString(R.string.title_no_host));
if (TextUtils.isEmpty(port))
port = (starttls ? "587" : "465");
port = (encryption == EmailService.ENCRYPTION_SSL ? "465" : "587");
if (TextUtils.isEmpty(user) && !should)
throw new IllegalArgumentException(context.getString(R.string.title_no_user));
if (synchronize && TextUtils.isEmpty(password) && !insecure && certificate == null && !should)
@ -779,7 +776,7 @@ public class FragmentIdentity extends FragmentBase {
return true;
if (!Objects.equals(identity.host, host))
return true;
if (!Objects.equals(identity.starttls, starttls))
if (!Objects.equals(identity.encryption, encryption))
return true;
if (!Objects.equals(identity.insecure, insecure))
return true;
@ -830,7 +827,7 @@ public class FragmentIdentity extends FragmentBase {
boolean check = (synchronize && (identity == null ||
!identity.synchronize || identity.error != null ||
!host.equals(identity.host) ||
starttls != identity.starttls ||
encryption != identity.encryption ||
insecure != identity.insecure ||
Integer.parseInt(port) != identity.port ||
!user.equals(identity.user) ||
@ -852,9 +849,10 @@ public class FragmentIdentity extends FragmentBase {
Long server_max_size = null;
if (check) {
// Create transport
String protocol = (starttls ? "smtp" : "smtps");
String protocol = (encryption == EmailService.ENCRYPTION_SSL ? "smtps" : "smtp");
try (EmailService iservice = new EmailService(
context, protocol, realm, insecure, EmailService.PURPOSE_CHECK, true)) {
context, protocol, realm, encryption, insecure,
EmailService.PURPOSE_CHECK, true)) {
iservice.setUseIp(use_ip, ehlo);
iservice.connect(
host, Integer.parseInt(port),
@ -887,7 +885,7 @@ public class FragmentIdentity extends FragmentBase {
identity.signature = signature;
identity.host = host;
identity.starttls = starttls;
identity.encryption = encryption;
identity.insecure = insecure;
identity.port = Integer.parseInt(port);
identity.auth_type = auth;
@ -1094,8 +1092,14 @@ public class FragmentIdentity extends FragmentBase {
signature = (identity == null ? null : identity.signature);
etHost.setText(identity == null ? null : identity.host);
rgEncryption.check(identity != null && identity.starttls ? R.id.radio_starttls : R.id.radio_ssl);
cbInsecure.setTag(identity == null ? false : identity.insecure);
if (identity != null && identity.encryption == EmailService.ENCRYPTION_STARTTLS)
rgEncryption.check(R.id.radio_starttls);
else if (identity != null && identity.encryption == EmailService.ENCRYPTION_NONE)
rgEncryption.check(R.id.radio_none);
else
rgEncryption.check(R.id.radio_ssl);
cbInsecure.setChecked(identity == null ? false : identity.insecure);
etPort.setText(identity == null ? null : Long.toString(identity.port));
etUser.setText(identity == null ? null : identity.user);
@ -1210,7 +1214,7 @@ public class FragmentIdentity extends FragmentBase {
EmailProvider provider = providers.get(pos);
if (provider.smtp.host.equals(identity.host) &&
provider.smtp.port == identity.port &&
provider.smtp.starttls == identity.starttls) {
provider.smtp.starttls == (identity.encryption == EmailService.ENCRYPTION_STARTTLS)) {
spProvider.setTag(pos);
spProvider.setSelection(pos);
break;

View File

@ -403,6 +403,7 @@ public class FragmentOAuth extends FragmentBase {
EmailProvider provider = EmailProvider.getProvider(context, id);
String aprotocol = (provider.imap.starttls ? "imap" : "imaps");
int aencryption = (provider.imap.starttls ? EmailService.ENCRYPTION_STARTTLS : EmailService.ENCRYPTION_SSL);
if (accessToken != null) {
String[] segments = accessToken.split("\\.");
@ -428,7 +429,8 @@ public class FragmentOAuth extends FragmentBase {
String email = jpayload.getString("email");
if (!TextUtils.isEmpty(email) && !email.equals(address)) {
try (EmailService iservice = new EmailService(
context, aprotocol, null, false, EmailService.PURPOSE_CHECK, true)) {
context, aprotocol, null, aencryption, false,
EmailService.PURPOSE_CHECK, true)) {
iservice.connect(
provider.imap.host, provider.imap.port,
EmailService.AUTH_TYPE_OAUTH, provider.id,
@ -477,7 +479,8 @@ public class FragmentOAuth extends FragmentBase {
Log.i("OAuth checking IMAP provider=" + provider.id);
try (EmailService iservice = new EmailService(
context, aprotocol, null, false, EmailService.PURPOSE_CHECK, true)) {
context, aprotocol, null, aencryption, false,
EmailService.PURPOSE_CHECK, true)) {
iservice.connect(
provider.imap.host, provider.imap.port,
EmailService.AUTH_TYPE_OAUTH, provider.id,
@ -490,8 +493,11 @@ public class FragmentOAuth extends FragmentBase {
Log.i("OAuth checking SMTP provider=" + provider.id);
Long max_size;
String iprotocol = (provider.smtp.starttls ? "smtp" : "smtps");
int iencryption = (provider.smtp.starttls ? EmailService.ENCRYPTION_STARTTLS : EmailService.ENCRYPTION_SSL);
try (EmailService iservice = new EmailService(
context, iprotocol, null, false, EmailService.PURPOSE_CHECK, true)) {
context, iprotocol, null, iencryption, false,
EmailService.PURPOSE_CHECK, true)) {
iservice.connect(
provider.smtp.host, provider.smtp.port,
EmailService.AUTH_TYPE_OAUTH, provider.id,
@ -512,7 +518,7 @@ public class FragmentOAuth extends FragmentBase {
EntityAccount account = new EntityAccount();
account.host = provider.imap.host;
account.starttls = provider.imap.starttls;
account.encryption = aencryption;
account.port = provider.imap.port;
account.auth_type = EmailService.AUTH_TYPE_OAUTH;
account.provider = provider.id;
@ -565,7 +571,7 @@ public class FragmentOAuth extends FragmentBase {
ident.account = account.id;
ident.host = provider.smtp.host;
ident.starttls = provider.smtp.starttls;
ident.encryption = iencryption;
ident.port = provider.smtp.port;
ident.auth_type = EmailService.AUTH_TYPE_OAUTH;
ident.provider = provider.id;

View File

@ -162,6 +162,13 @@ public class FragmentPop extends FragmentBase {
pbWait = view.findViewById(R.id.pbWait);
rgEncryption.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int id) {
etPort.setHint(id == R.id.radio_ssl ? "995" : "110");
}
});
tilPassword.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@ -247,8 +254,16 @@ public class FragmentPop extends FragmentBase {
Bundle args = new Bundle();
args.putLong("id", id);
int encryption;
if (rgEncryption.getCheckedRadioButtonId() == R.id.radio_starttls)
encryption = EmailService.ENCRYPTION_STARTTLS;
else if (rgEncryption.getCheckedRadioButtonId() == R.id.radio_none)
encryption = EmailService.ENCRYPTION_NONE;
else
encryption = EmailService.ENCRYPTION_SSL;
args.putString("host", etHost.getText().toString().trim());
args.putBoolean("starttls", rgEncryption.getCheckedRadioButtonId() == R.id.radio_starttls);
args.putInt("encryption", encryption);
args.putBoolean("insecure", cbInsecure.isChecked());
args.putString("port", etPort.getText().toString());
args.putString("user", etUser.getText().toString());
@ -278,6 +293,7 @@ public class FragmentPop extends FragmentBase {
saving = true;
getActivity().invalidateOptionsMenu();
Helper.setViewsEnabled(view, false);
pbSave.setVisibility(View.VISIBLE);
grpError.setVisibility(View.GONE);
}
@ -294,7 +310,7 @@ public class FragmentPop extends FragmentBase {
long id = args.getLong("id");
String host = args.getString("host");
boolean starttls = args.getBoolean("starttls");
int encryption = args.getInt("encryption");
boolean insecure = args.getBoolean("insecure");
String port = args.getString("port");
String user = args.getString("user").trim();
@ -327,7 +343,7 @@ public class FragmentPop extends FragmentBase {
if (TextUtils.isEmpty(host))
throw new IllegalArgumentException(context.getString(R.string.title_no_host));
if (TextUtils.isEmpty(port))
port = "995";
port = (encryption == EmailService.ENCRYPTION_SSL ? "995" : "110");
if (TextUtils.isEmpty(user))
throw new IllegalArgumentException(context.getString(R.string.title_no_user));
if (synchronize && TextUtils.isEmpty(password) && !insecure)
@ -349,7 +365,7 @@ public class FragmentPop extends FragmentBase {
!account.synchronize ||
account.error != null ||
!account.host.equals(host) ||
!account.starttls.equals(starttls) ||
!account.encryption.equals(encryption) ||
!account.insecure.equals(insecure) ||
!account.port.equals(Integer.parseInt(port)) ||
!account.user.equals(user) ||
@ -362,9 +378,10 @@ public class FragmentPop extends FragmentBase {
// Check POP3 server
if (check) {
String protocol = "pop3" + (starttls ? "" : "s");
String protocol = "pop3" + (encryption == EmailService.ENCRYPTION_SSL ? "s" : "");
try (EmailService iservice = new EmailService(
context, protocol, null, insecure, EmailService.PURPOSE_CHECK, true)) {
context, protocol, null, encryption, insecure,
EmailService.PURPOSE_CHECK, true)) {
iservice.connect(
host, Integer.parseInt(port),
EmailService.AUTH_TYPE_PASSWORD, null,
@ -389,7 +406,7 @@ public class FragmentPop extends FragmentBase {
account.protocol = EntityAccount.TYPE_POP;
account.host = host;
account.starttls = starttls;
account.encryption = encryption;
account.insecure = insecure;
account.port = Integer.parseInt(port);
account.auth_type = EmailService.AUTH_TYPE_PASSWORD;
@ -571,7 +588,13 @@ public class FragmentPop extends FragmentBase {
etHost.setText(account == null ? null : account.host);
etPort.setText(account == null ? null : Long.toString(account.port));
rgEncryption.check(account != null && account.starttls ? R.id.radio_starttls : R.id.radio_ssl);
if (account != null && account.encryption == EmailService.ENCRYPTION_STARTTLS)
rgEncryption.check(R.id.radio_starttls);
else if (account != null && account.encryption == EmailService.ENCRYPTION_NONE)
rgEncryption.check(R.id.radio_none);
else
rgEncryption.check(R.id.radio_ssl);
cbInsecure.setChecked(account == null ? false : account.insecure);
etUser.setText(account == null ? null : account.user);

View File

@ -297,9 +297,10 @@ public class FragmentQuickSetup extends FragmentBase {
List<EntityFolder> folders;
String aprotocol = provider.imap.starttls ? "imap" : "imaps";
String aprotocol = (provider.imap.starttls ? "imap" : "imaps");
int aencryption = (provider.imap.starttls ? EmailService.ENCRYPTION_STARTTLS : EmailService.ENCRYPTION_SSL);
try (EmailService iservice = new EmailService(
context, aprotocol, null, false, EmailService.PURPOSE_CHECK, true)) {
context, aprotocol, null, aencryption, false, EmailService.PURPOSE_CHECK, true)) {
try {
iservice.connect(
provider.imap.host, provider.imap.port,
@ -340,9 +341,11 @@ public class FragmentQuickSetup extends FragmentBase {
}
Long max_size = null;
String iprotocol = provider.smtp.starttls ? "smtp" : "smtps";
String iprotocol = (provider.smtp.starttls ? "smtp" : "smtps");
int iencryption = (provider.smtp.starttls ? EmailService.ENCRYPTION_STARTTLS : EmailService.ENCRYPTION_SSL);
try (EmailService iservice = new EmailService(
context, iprotocol, null, false, EmailService.PURPOSE_CHECK, true)) {
context, iprotocol, null, iencryption, false,
EmailService.PURPOSE_CHECK, true)) {
iservice.setUseIp(provider.useip, null);
iservice.connect(
provider.smtp.host, provider.smtp.port,
@ -373,7 +376,7 @@ public class FragmentQuickSetup extends FragmentBase {
EntityAccount account = new EntityAccount();
account.host = provider.imap.host;
account.starttls = provider.imap.starttls;
account.encryption = aencryption;
account.port = provider.imap.port;
account.auth_type = EmailService.AUTH_TYPE_PASSWORD;
account.user = user;
@ -423,7 +426,7 @@ public class FragmentQuickSetup extends FragmentBase {
identity.account = account.id;
identity.host = provider.smtp.host;
identity.starttls = provider.smtp.starttls;
identity.encryption = iencryption;
identity.port = provider.smtp.port;
identity.auth_type = EmailService.AUTH_TYPE_PASSWORD;
identity.user = user;

View File

@ -582,7 +582,7 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar
// Create transport
try (EmailService iservice = new EmailService(
this, ident.getProtocol(), ident.realm, ident.insecure, debug)) {
this, ident.getProtocol(), ident.realm, ident.encryption, ident.insecure, debug)) {
iservice.setUseIp(ident.use_ip, ident.ehlo);
iservice.setUnicode(ident.unicode);

View File

@ -909,7 +909,7 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
boolean debug = (prefs.getBoolean("debug", false) || BuildConfig.DEBUG);
final EmailService iservice = new EmailService(
this, account.getProtocol(), account.realm, account.insecure, debug);
this, account.getProtocol(), account.realm, account.encryption, account.insecure, debug);
iservice.setPartialFetch(account.partial_fetch);
iservice.setIgnoreBodyStructureSize(account.ignore_size);
if (account.protocol != EntityAccount.TYPE_IMAP)

View File

@ -31,7 +31,7 @@ public class TupleAccountState extends EntityAccount {
if (obj instanceof TupleAccountState) {
TupleAccountState other = (TupleAccountState) obj;
return (this.host.equals(other.host) &&
this.starttls.equals(other.starttls) &&
this.encryption.equals(other.encryption) &&
this.insecure.equals(other.insecure) &&
this.port.equals(other.port) &&
this.user.equals(other.user) &&

View File

@ -137,28 +137,43 @@
<!-- SSL/STARTTLS -->
<eu.faircode.email.FixedTextView
android:id="@+id/tvEncryption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_encryption"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etHost" />
<RadioGroup
android:id="@+id/rgEncryption"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:orientation="horizontal"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etHost">
app:layout_constraintTop_toBottomOf="@id/tvEncryption">
<RadioButton
android:id="@+id/radio_ssl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title_ssl" />
android:text="@string/title_encryption_ssl" />
<RadioButton
android:id="@+id/radio_starttls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@string/title_starttls" />
android:text="@string/title_encryption_starttls" />
<RadioButton
android:id="@+id/radio_none"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title_encryption_none"
android:textColor="?attr/colorWarning" />
</RadioGroup>
<CheckBox
@ -167,6 +182,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_allow_insecure"
android:textColor="?attr/colorWarning"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/rgEncryption" />
@ -966,7 +982,7 @@
android:layout_height="0dp"
app:constraint_referenced_ids="
tvDomain,tvDomainHint,etDomain,btnAutoConfig,
tvImap,tvHost,etHost,rgEncryption,cbInsecure,tvInsecureRemark,tvPort,etPort" />
tvImap,tvHost,etHost,tvEncryption,rgEncryption,cbInsecure,tvInsecureRemark,tvPort,etPort" />
<androidx.constraintlayout.widget.Group
android:id="@+id/grpAuthorize"

View File

@ -283,28 +283,43 @@
<!-- SSL/STARTTLS -->
<eu.faircode.email.FixedTextView
android:id="@+id/tvEncryption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_encryption"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etHost" />
<RadioGroup
android:id="@+id/rgEncryption"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:orientation="horizontal"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etHost">
app:layout_constraintTop_toBottomOf="@id/tvEncryption">
<RadioButton
android:id="@+id/radio_ssl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title_ssl" />
android:text="@string/title_encryption_ssl" />
<RadioButton
android:id="@+id/radio_starttls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@string/title_starttls" />
android:text="@string/title_encryption_starttls" />
<RadioButton
android:id="@+id/radio_none"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title_encryption_none"
android:textColor="?attr/colorWarning" />
</RadioGroup>
<CheckBox
@ -313,6 +328,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_allow_insecure"
android:textColor="?attr/colorWarning"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/rgEncryption" />
@ -832,7 +848,7 @@
app:constraint_referenced_ids="
tvProvider,spProvider,
tvDomain,tvDomainHint,etDomain,btnAutoConfig,
tvSmtp,tvHost,etHost,rgEncryption,cbInsecure,tvInsecureRemark,tvPort,etPort,
tvSmtp,tvHost,etHost,tvEncryption,rgEncryption,cbInsecure,tvInsecureRemark,tvPort,etPort,
tvUser,etUser,tvPassword,tilPassword,tvCaseSensitiveHint,btnCertificate,tvCertificate,btnOAuth,
tvRealm,etRealm,
cbUseIp,tvUseIpHint,tvEhlo,etEhlo,

View File

@ -48,28 +48,43 @@
<!-- SSL/STARTTLS -->
<eu.faircode.email.FixedTextView
android:id="@+id/tvEncryption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_encryption"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etHost" />
<RadioGroup
android:id="@+id/rgEncryption"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:orientation="horizontal"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etHost">
app:layout_constraintTop_toBottomOf="@id/tvEncryption">
<RadioButton
android:id="@+id/radio_ssl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title_ssl" />
android:text="@string/title_encryption_ssl" />
<RadioButton
android:id="@+id/radio_starttls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@string/title_starttls" />
android:text="@string/title_encryption_starttls" />
<RadioButton
android:id="@+id/radio_none"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title_encryption_none"
android:textColor="?attr/colorWarning" />
</RadioGroup>
<CheckBox
@ -78,6 +93,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_allow_insecure"
android:textColor="?attr/colorWarning"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/rgEncryption" />

View File

@ -619,8 +619,10 @@
<string name="title_provider">Provider</string>
<string name="title_custom">Custom</string>
<string name="title_host">Host name</string>
<string name="title_ssl" translatable="false">SSL/TLS</string>
<string name="title_starttls" translatable="false">STARTTLS</string>
<string name="title_encryption">Encryption</string>
<string name="title_encryption_ssl" translatable="false">SSL/TLS</string>
<string name="title_encryption_starttls" translatable="false">STARTTLS</string>
<string name="title_encryption_none">None</string>
<string name="title_allow_insecure">Allow insecure connections</string>
<string name="title_insecure_remark">Insecure connections should only be allowed on trusted networks and never on public networks</string>
<string name="title_port">Port number</string>