Added option for custom EHLO identification

pull/178/head
M66B 5 years ago
parent 4493d77d33
commit 6110ff0aa6

File diff suppressed because it is too large Load Diff

@ -61,7 +61,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 = 153, version = 154,
entities = { entities = {
EntityIdentity.class, EntityIdentity.class,
EntityAccount.class, EntityAccount.class,
@ -1538,6 +1538,13 @@ public abstract class DB extends RoomDatabase {
Log.i("DB migration from version " + startVersion + " to " + endVersion); Log.i("DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `identity` ADD COLUMN `unicode` INTEGER NOT NULL DEFAULT 0"); db.execSQL("ALTER TABLE `identity` ADD COLUMN `unicode` INTEGER NOT NULL DEFAULT 0");
} }
})
.addMigrations(new Migration(153, 154) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase db) {
Log.i("DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `identity` ADD COLUMN `ehlo` TEXT");
}
}); });
} }

@ -84,6 +84,7 @@ public class EmailService implements AutoCloseable {
private boolean insecure; private boolean insecure;
private boolean harden; private boolean harden;
private boolean useip; private boolean useip;
private String ehlo;
private boolean debug; private boolean debug;
private Properties properties; private Properties properties;
private Session isession; private Session isession;
@ -231,8 +232,9 @@ public class EmailService implements AutoCloseable {
properties.put("mail." + protocol + ".ignorebodystructuresize", Boolean.toString(enabled)); properties.put("mail." + protocol + ".ignorebodystructuresize", Boolean.toString(enabled));
} }
void setUseIp(boolean enabled) { void setUseIp(boolean enabled, String host) {
useip = enabled; this.useip = enabled;
this.ehlo = host;
} }
void setLeaveOnServer(boolean keep) { void setLeaveOnServer(boolean keep) {
@ -503,14 +505,15 @@ public class EmailService implements AutoCloseable {
// https://tools.ietf.org/html/rfc5321#section-4.1.3 // https://tools.ietf.org/html/rfc5321#section-4.1.3
String haddr = (address instanceof Inet4Address ? "[127.0.0.1]" : "[IPv6:::1]"); String haddr = (address instanceof Inet4Address ? "[127.0.0.1]" : "[IPv6:::1]");
properties.put("mail." + protocol + ".localhost", useip ? haddr : hdomain); properties.put("mail." + protocol + ".localhost",
ehlo == null ? (useip ? haddr : hdomain) : ehlo);
Log.i("Using localhost=" + properties.getProperty("mail." + protocol + ".localhost")); Log.i("Using localhost=" + properties.getProperty("mail." + protocol + ".localhost"));
iservice = isession.getTransport(protocol); iservice = isession.getTransport(protocol);
try { try {
iservice.connect(address.getHostAddress(), port, user, password); iservice.connect(address.getHostAddress(), port, user, password);
} catch (MessagingException ex) { } catch (MessagingException ex) {
if (ConnectionHelper.isSyntacticallyInvalid(ex)) { if (ehlo == null && ConnectionHelper.isSyntacticallyInvalid(ex)) {
properties.put("mail." + protocol + ".localhost", useip ? hdomain : haddr); properties.put("mail." + protocol + ".localhost", useip ? hdomain : haddr);
Log.i("Fallback localhost=" + properties.getProperty("mail." + protocol + ".localhost")); Log.i("Fallback localhost=" + properties.getProperty("mail." + protocol + ".localhost"));
try { try {

@ -84,6 +84,7 @@ public class EntityIdentity {
public String fingerprint; public String fingerprint;
@NonNull @NonNull
public Boolean use_ip = true; // instead of domain name public Boolean use_ip = true; // instead of domain name
public String ehlo;
@NonNull @NonNull
public Boolean synchronize; public Boolean synchronize;
@NonNull @NonNull
@ -183,6 +184,7 @@ public class EntityIdentity {
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);
json.put("ehlo", ehlo);
json.put("synchronize", synchronize); json.put("synchronize", synchronize);
json.put("primary", primary); json.put("primary", primary);
@ -234,6 +236,8 @@ public class EntityIdentity {
identity.fingerprint = json.getString("fingerprint"); identity.fingerprint = json.getString("fingerprint");
if (json.has("use_ip")) if (json.has("use_ip"))
identity.use_ip = json.getBoolean("use_ip"); identity.use_ip = json.getBoolean("use_ip");
if (json.has("ehlo"))
identity.ehlo = json.getString("ehlo");
identity.synchronize = json.getBoolean("synchronize"); identity.synchronize = json.getBoolean("synchronize");
identity.primary = json.getBoolean("primary"); identity.primary = json.getBoolean("primary");
@ -271,6 +275,7 @@ public class EntityIdentity {
this.password.equals(other.password) && this.password.equals(other.password) &&
Objects.equals(this.realm, other.realm) && Objects.equals(this.realm, other.realm) &&
this.use_ip == other.use_ip && this.use_ip == other.use_ip &&
Objects.equals(this.ehlo, other.ehlo) &&
this.synchronize.equals(other.synchronize) && this.synchronize.equals(other.synchronize) &&
this.primary.equals(other.primary) && this.primary.equals(other.primary) &&
this.sender_extra.equals(sender_extra) && this.sender_extra.equals(sender_extra) &&

@ -102,6 +102,7 @@ public class FragmentIdentity extends FragmentBase {
private Button btnOAuth; private Button btnOAuth;
private EditText etRealm; private EditText etRealm;
private CheckBox cbUseIp; private CheckBox cbUseIp;
private EditText etEhlo;
private CheckBox cbSynchronize; private CheckBox cbSynchronize;
private CheckBox cbPrimary; private CheckBox cbPrimary;
@ -192,6 +193,7 @@ public class FragmentIdentity extends FragmentBase {
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);
etEhlo = view.findViewById(R.id.etEhlo);
cbSynchronize = view.findViewById(R.id.cbSynchronize); cbSynchronize = view.findViewById(R.id.cbSynchronize);
cbPrimary = view.findViewById(R.id.cbPrimary); cbPrimary = view.findViewById(R.id.cbPrimary);
@ -258,6 +260,7 @@ public class FragmentIdentity extends FragmentBase {
etPort.setText(Integer.toString(provider.smtp.port)); etPort.setText(Integer.toString(provider.smtp.port));
rgEncryption.check(provider.smtp.starttls ? R.id.radio_starttls : R.id.radio_ssl); rgEncryption.check(provider.smtp.starttls ? R.id.radio_starttls : R.id.radio_ssl);
cbUseIp.setChecked(provider.useip); cbUseIp.setChecked(provider.useip);
etEhlo.setText(null);
break; break;
} }
@ -379,6 +382,7 @@ public class FragmentIdentity extends FragmentBase {
etPort.setText(position == 0 ? null : Integer.toString(provider.smtp.port)); etPort.setText(position == 0 ? null : Integer.toString(provider.smtp.port));
rgEncryption.check(provider.smtp.starttls ? R.id.radio_starttls : R.id.radio_ssl); rgEncryption.check(provider.smtp.starttls ? R.id.radio_starttls : R.id.radio_ssl);
cbUseIp.setChecked(provider.useip); cbUseIp.setChecked(provider.useip);
etEhlo.setText(null);
EntityAccount account = (EntityAccount) spAccount.getSelectedItem(); EntityAccount account = (EntityAccount) spAccount.getSelectedItem();
if (account == null || if (account == null ||
@ -537,6 +541,7 @@ public class FragmentIdentity extends FragmentBase {
etPort.setText(Integer.toString(provider.smtp.port)); etPort.setText(Integer.toString(provider.smtp.port));
rgEncryption.check(provider.smtp.starttls ? R.id.radio_starttls : R.id.radio_ssl); rgEncryption.check(provider.smtp.starttls ? R.id.radio_starttls : R.id.radio_ssl);
cbUseIp.setChecked(provider.useip); cbUseIp.setChecked(provider.useip);
etEhlo.setText(null);
} }
@Override @Override
@ -593,6 +598,7 @@ public class FragmentIdentity extends FragmentBase {
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());
args.putString("ehlo", etEhlo.getText().toString());
args.putString("signature", signature); args.putString("signature", signature);
args.putBoolean("synchronize", cbSynchronize.isChecked()); args.putBoolean("synchronize", cbSynchronize.isChecked());
args.putBoolean("primary", cbPrimary.isChecked()); args.putBoolean("primary", cbPrimary.isChecked());
@ -643,6 +649,7 @@ public class FragmentIdentity extends FragmentBase {
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");
String ehlo = args.getString("ehlo");
boolean synchronize = args.getBoolean("synchronize"); boolean synchronize = args.getBoolean("synchronize");
boolean primary = args.getBoolean("primary"); boolean primary = args.getBoolean("primary");
@ -705,6 +712,9 @@ public class FragmentIdentity extends FragmentBase {
if (TextUtils.isEmpty(realm)) if (TextUtils.isEmpty(realm))
realm = null; realm = null;
if (TextUtils.isEmpty(ehlo))
ehlo = null;
if (TextUtils.isEmpty(sender_extra_regex)) if (TextUtils.isEmpty(sender_extra_regex))
sender_extra_regex = null; sender_extra_regex = null;
@ -763,6 +773,8 @@ public class FragmentIdentity extends FragmentBase {
return true; return true;
if (!Objects.equals(identity.use_ip, use_ip)) if (!Objects.equals(identity.use_ip, use_ip))
return true; return true;
if (!Objects.equals(identity.ehlo, ehlo))
return true;
if (!Objects.equals(identity.synchronize, synchronize)) if (!Objects.equals(identity.synchronize, synchronize))
return true; return true;
if (!Objects.equals(identity.primary, (identity.synchronize && primary))) if (!Objects.equals(identity.primary, (identity.synchronize && primary)))
@ -793,7 +805,8 @@ public class FragmentIdentity extends FragmentBase {
!Objects.equals(identity.certificate_alias, 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) ||
!Objects.equals(identity.ehlo, ehlo));
Log.i("Identity check=" + check); Log.i("Identity check=" + check);
Long last_connected = null; Long last_connected = null;
@ -806,7 +819,7 @@ public class FragmentIdentity extends FragmentBase {
String protocol = (starttls ? "smtp" : "smtps"); String protocol = (starttls ? "smtp" : "smtps");
try (EmailService iservice = new EmailService( try (EmailService iservice = new EmailService(
context, protocol, realm, insecure, EmailService.PURPOSE_CHECK, true)) { context, protocol, realm, insecure, EmailService.PURPOSE_CHECK, true)) {
iservice.setUseIp(use_ip); iservice.setUseIp(use_ip, ehlo);
iservice.connect( iservice.connect(
host, Integer.parseInt(port), host, Integer.parseInt(port),
auth, provider, auth, provider,
@ -848,6 +861,7 @@ public class FragmentIdentity extends FragmentBase {
identity.realm = realm; identity.realm = realm;
identity.fingerprint = fingerprint; identity.fingerprint = fingerprint;
identity.use_ip = use_ip; identity.use_ip = use_ip;
identity.ehlo = ehlo;
identity.synchronize = synchronize; identity.synchronize = synchronize;
identity.primary = (identity.synchronize && primary); identity.primary = (identity.synchronize && primary);
@ -1056,6 +1070,7 @@ public class FragmentIdentity extends FragmentBase {
} }
cbUseIp.setChecked(identity == null ? true : identity.use_ip); cbUseIp.setChecked(identity == null ? true : identity.use_ip);
etEhlo.setText(identity == null ? null : identity.ehlo);
cbSynchronize.setChecked(identity == null ? true : identity.synchronize); cbSynchronize.setChecked(identity == null ? true : identity.synchronize);
cbPrimary.setChecked(identity == null ? true : identity.primary); cbPrimary.setChecked(identity == null ? true : identity.primary);

@ -308,7 +308,7 @@ public class FragmentQuickSetup extends FragmentBase {
String iprotocol = provider.smtp.starttls ? "smtp" : "smtps"; String iprotocol = provider.smtp.starttls ? "smtp" : "smtps";
try (EmailService iservice = new EmailService( try (EmailService iservice = new EmailService(
context, iprotocol, null, false, EmailService.PURPOSE_CHECK, true)) { context, iprotocol, null, false, EmailService.PURPOSE_CHECK, true)) {
iservice.setUseIp(provider.useip); iservice.setUseIp(provider.useip, null);
iservice.connect( iservice.connect(
provider.smtp.host, provider.smtp.port, provider.smtp.host, provider.smtp.port,
EmailService.AUTH_TYPE_PASSWORD, null, EmailService.AUTH_TYPE_PASSWORD, null,

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

@ -488,6 +488,28 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbUseIp" /> app:layout_constraintTop_toBottomOf="@id/cbUseIp" />
<eu.faircode.email.FixedTextView
android:id="@+id/tvEhlo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_ehlo"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvUseIpHint" />
<eu.faircode.email.EditTextPlain
android:id="@+id/etEhlo"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:autofillHints="name"
android:hint="@string/title_optional"
android:inputType="text"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvEhlo" />
<CheckBox <CheckBox
android:id="@+id/cbSynchronize" android:id="@+id/cbSynchronize"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -495,7 +517,7 @@
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:text="@string/title_enabled" android:text="@string/title_enabled"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvUseIpHint" /> app:layout_constraintTop_toBottomOf="@id/etEhlo" />
<CheckBox <CheckBox
android:id="@+id/cbPrimary" android:id="@+id/cbPrimary"
@ -760,7 +782,7 @@
tvSmtp,tvHost,etHost,rgEncryption,cbInsecure,tvInsecureRemark,tvPort,etPort, tvSmtp,tvHost,etHost,rgEncryption,cbInsecure,tvInsecureRemark,tvPort,etPort,
tvUser,etUser,tvPassword,tilPassword,btnCertificate,tvCertificate,btnOAuth, tvUser,etUser,tvPassword,tilPassword,btnCertificate,tvCertificate,btnOAuth,
tvRealm,etRealm, tvRealm,etRealm,
cbUseIp,tvUseIpHint, cbUseIp,tvUseIpHint,tvEhlo,etEhlo,
cbSynchronize,cbPrimary, cbSynchronize,cbPrimary,
cbSenderExtra,tvSenderExtra,etSenderExtra,tvSenderExtraHint, cbSenderExtra,tvSenderExtra,etSenderExtra,tvSenderExtraHint,
tvReplyTo,etReplyTo,tvCc,etCc,tvCcHint,tvBcc,etBcc,tvBccHint,cbUnicode" /> tvReplyTo,etReplyTo,tvCc,etCc,tvCcHint,tvBcc,etBcc,tvBccHint,cbUnicode" />

@ -573,6 +573,7 @@
<string name="title_client_certificate">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_ehlo">Custom HELO/EHLO identification</string>
<string name="title_primary">Primary</string> <string name="title_primary">Primary</string>
<string name="title_primary_account">Primary (default account)</string> <string name="title_primary_account">Primary (default account)</string>
<string name="title_primary_identity">Primary (default identity)</string> <string name="title_primary_identity">Primary (default identity)</string>

Loading…
Cancel
Save