Browse Source

Allow insecure connections

main
M66B 6 years ago
parent
commit
7d642f468d
16 changed files with 1226 additions and 69 deletions
  1. +6
    -6
      FAQ.md
  2. +1034
    -0
      app/schemas/eu.faircode.email.DB/23.json
  3. +10
    -1
      app/src/main/java/eu/faircode/email/DB.java
  4. +4
    -0
      app/src/main/java/eu/faircode/email/EntityAccount.java
  5. +4
    -2
      app/src/main/java/eu/faircode/email/EntityIdentity.java
  6. +45
    -12
      app/src/main/java/eu/faircode/email/FragmentAccount.java
  7. +4
    -4
      app/src/main/java/eu/faircode/email/FragmentFolder.java
  8. +31
    -20
      app/src/main/java/eu/faircode/email/FragmentIdentity.java
  9. +10
    -0
      app/src/main/java/eu/faircode/email/FragmentOptions.java
  10. +28
    -12
      app/src/main/java/eu/faircode/email/MessageHelper.java
  11. +3
    -3
      app/src/main/java/eu/faircode/email/ServiceSynchronize.java
  12. +2
    -2
      app/src/main/java/eu/faircode/email/ViewModelBrowse.java
  13. +23
    -5
      app/src/main/res/layout/fragment_account.xml
  14. +10
    -1
      app/src/main/res/layout/fragment_identity.xml
  15. +11
    -1
      app/src/main/res/layout/fragment_options.xml
  16. +1
    -0
      app/src/main/res/values/strings.xml

+ 6
- 6
FAQ.md View File

@ -99,15 +99,15 @@ This IMAP extension is required to implement two way synchronization, which is n
So, unless your provider can enable this extension, you cannot use FairEmail for this provider. So, unless your provider can enable this extension, you cannot use FairEmail for this provider.
<a name="FAQ11"></a> <a name="FAQ11"></a>
**(11) Why is STARTTLS for IMAP not supported?**
~~**(11) Why is STARTTLS for IMAP not supported?**~~
STARTTLS starts with a not encrypted connection and is therefore not secure.
All well known IMAP servers support IMAP with a plain SSL connection, so there is no need to support STARTTLS for IMAP.
If you encounter an IMAP server that requires STARTTLS, please let me know.
~~STARTTLS starts with a not encrypted connection and is therefore not secure.~~
~~All well known IMAP servers support IMAP with a plain SSL connection, so there is no need to support STARTTLS for IMAP.~~
~~If you encounter an IMAP server that requires STARTTLS, please let me know.~~
For more background information, please see [this article](https://www.eff.org/nl/deeplinks/2018/06/announcing-starttls-everywhere-securing-hop-hop-email-delivery).
~~For more background information, please see [this article](https://www.eff.org/nl/deeplinks/2018/06/announcing-starttls-everywhere-securing-hop-hop-email-delivery).~~
tl;dr; "*Additionally, even if you configure STARTTLS perfectly and use a valid certificate, there’s still no guarantee your communication will be encrypted.*"
~~tl;dr; "*Additionally, even if you configure STARTTLS perfectly and use a valid certificate, there’s still no guarantee your communication will be encrypted.*"~~
<a name="FAQ13"></a> <a name="FAQ13"></a>
**(13) How does search on server work?** **(13) How does search on server work?**


+ 1034
- 0
app/schemas/eu.faircode.email.DB/23.json
File diff suppressed because it is too large
View File


+ 10
- 1
app/src/main/java/eu/faircode/email/DB.java View File

@ -45,7 +45,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase;
// https://developer.android.com/topic/libraries/architecture/room.html // https://developer.android.com/topic/libraries/architecture/room.html
@Database( @Database(
version = 22,
version = 23,
entities = { entities = {
EntityIdentity.class, EntityIdentity.class,
EntityAccount.class, EntityAccount.class,
@ -275,6 +275,15 @@ public abstract class DB extends RoomDatabase {
db.execSQL("CREATE UNIQUE INDEX `index_message_msgid_folder_ui_found` ON `message` (`msgid`, `folder`, `ui_found`)"); db.execSQL("CREATE UNIQUE INDEX `index_message_msgid_folder_ui_found` ON `message` (`msgid`, `folder`, `ui_found`)");
} }
}) })
.addMigrations(new Migration(22, 23) {
@Override
public void migrate(SupportSQLiteDatabase db) {
Log.i(Helper.TAG, "DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `account` ADD COLUMN `starttls` INTEGER NOT NULL DEFAULT 0");
db.execSQL("ALTER TABLE `account` ADD COLUMN `insecure` INTEGER NOT NULL DEFAULT 0");
db.execSQL("ALTER TABLE `identity` ADD COLUMN `insecure` INTEGER NOT NULL DEFAULT 0");
}
})
.build(); .build();
} }


+ 4
- 0
app/src/main/java/eu/faircode/email/EntityAccount.java View File

@ -41,6 +41,10 @@ public class EntityAccount {
@NonNull @NonNull
public String host; // IMAP public String host; // IMAP
@NonNull @NonNull
public Boolean starttls;
@NonNull
public Boolean insecure;
@NonNull
public Integer port; public Integer port;
@NonNull @NonNull
public String user; public String user;


+ 4
- 2
app/src/main/java/eu/faircode/email/EntityIdentity.java View File

@ -54,10 +54,12 @@ public class EntityIdentity {
@NonNull @NonNull
public String host; // SMTP public String host; // SMTP
@NonNull @NonNull
public Integer port;
@NonNull
public Boolean starttls; public Boolean starttls;
@NonNull @NonNull
public Boolean insecure;
@NonNull
public Integer port;
@NonNull
public String user; public String user;
@NonNull @NonNull
public String password; public String password;


+ 45
- 12
app/src/main/java/eu/faircode/email/FragmentAccount.java View File

@ -28,12 +28,14 @@ import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.GradientDrawable;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.preference.PreferenceManager;
import android.text.Editable; import android.text.Editable;
import android.text.Html; import android.text.Html;
import android.text.TextUtils; import android.text.TextUtils;
@ -93,14 +95,19 @@ public class FragmentAccount extends FragmentEx {
private ViewGroup view; private ViewGroup view;
private Spinner spProvider; private Spinner spProvider;
private EditText etHost;
private EditText etDomain; private EditText etDomain;
private Button btnAutoConfig; private Button btnAutoConfig;
private EditText etHost;
private CheckBox cbStartTls;
private CheckBox cbInsecure;
private EditText etPort; private EditText etPort;
private Button btnAuthorize;
private EditText etUser; private EditText etUser;
private TextInputLayout tilPassword; private TextInputLayout tilPassword;
private Button btnAuthorize;
private Button btnAdvanced; private Button btnAdvanced;
private TextView tvName; private TextView tvName;
@ -155,6 +162,9 @@ public class FragmentAccount extends FragmentEx {
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
setSubtitle(R.string.title_edit_account); setSubtitle(R.string.title_edit_account);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
final boolean insecure = prefs.getBoolean("insecure", false);
view = (ViewGroup) inflater.inflate(R.layout.fragment_account, container, false); view = (ViewGroup) inflater.inflate(R.layout.fragment_account, container, false);
// Get controls // Get controls
@ -165,11 +175,13 @@ public class FragmentAccount extends FragmentEx {
etHost = view.findViewById(R.id.etHost); etHost = view.findViewById(R.id.etHost);
etPort = view.findViewById(R.id.etPort); etPort = view.findViewById(R.id.etPort);
btnAuthorize = view.findViewById(R.id.btnAuthorize);
cbStartTls = view.findViewById(R.id.cbStartTls);
cbInsecure = view.findViewById(R.id.cbInsecure);
etUser = view.findViewById(R.id.etUser); etUser = view.findViewById(R.id.etUser);
tilPassword = view.findViewById(R.id.tilPassword); tilPassword = view.findViewById(R.id.tilPassword);
btnAuthorize = view.findViewById(R.id.btnAuthorize);
btnAdvanced = view.findViewById(R.id.btnAdvanced); btnAdvanced = view.findViewById(R.id.btnAdvanced);
etName = view.findViewById(R.id.etName); etName = view.findViewById(R.id.etName);
@ -214,6 +226,8 @@ public class FragmentAccount extends FragmentEx {
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) { public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
Provider provider = (Provider) adapterView.getSelectedItem(); Provider provider = (Provider) adapterView.getSelectedItem();
grpServer.setVisibility(position == 1 ? View.VISIBLE : View.GONE); grpServer.setVisibility(position == 1 ? View.VISIBLE : View.GONE);
cbStartTls.setVisibility(position == 1 && insecure ? View.VISIBLE : View.GONE);
cbInsecure.setVisibility(position == 1 && insecure ? View.VISIBLE : View.GONE);
grpAuthorize.setVisibility(position > 0 ? View.VISIBLE : View.GONE); grpAuthorize.setVisibility(position > 0 ? View.VISIBLE : View.GONE);
btnAuthorize.setVisibility(provider.type == null ? View.GONE : View.VISIBLE); btnAuthorize.setVisibility(provider.type == null ? View.GONE : View.VISIBLE);
@ -293,6 +307,13 @@ public class FragmentAccount extends FragmentEx {
} }
}); });
cbStartTls.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
etPort.setHint(checked ? "143" : "993");
}
});
tilPassword.getEditText().addTextChangedListener(new TextWatcher() { tilPassword.getEditText().addTextChangedListener(new TextWatcher() {
@Override @Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@ -401,6 +422,8 @@ public class FragmentAccount extends FragmentEx {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putLong("id", id); args.putLong("id", id);
args.putString("host", etHost.getText().toString()); args.putString("host", etHost.getText().toString());
args.putBoolean("starttls", cbStartTls.isChecked());
args.putBoolean("insecure", cbInsecure.isChecked());
args.putString("port", etPort.getText().toString()); args.putString("port", etPort.getText().toString());
args.putString("user", etUser.getText().toString()); args.putString("user", etUser.getText().toString());
args.putString("password", tilPassword.getEditText().getText().toString()); args.putString("password", tilPassword.getEditText().getText().toString());
@ -411,6 +434,8 @@ public class FragmentAccount extends FragmentEx {
protected CheckResult onLoad(Context context, Bundle args) throws Throwable { protected CheckResult onLoad(Context context, Bundle args) throws Throwable {
long id = args.getLong("id"); long id = args.getLong("id");
String host = args.getString("host"); String host = args.getString("host");
boolean starttls = args.getBoolean("starttls");
boolean insecure = args.getBoolean("insecure");
String port = args.getString("port"); String port = args.getString("port");
String user = args.getString("user"); String user = args.getString("user");
String password = args.getString("password"); String password = args.getString("password");
@ -419,22 +444,22 @@ public class FragmentAccount extends FragmentEx {
if (TextUtils.isEmpty(host)) if (TextUtils.isEmpty(host))
throw new Throwable(getContext().getString(R.string.title_no_host)); throw new Throwable(getContext().getString(R.string.title_no_host));
if (TextUtils.isEmpty(port)) if (TextUtils.isEmpty(port))
port = "993";
port = (starttls ? "143" : "993");
if (TextUtils.isEmpty(user)) if (TextUtils.isEmpty(user))
throw new Throwable(getContext().getString(R.string.title_no_user)); throw new Throwable(getContext().getString(R.string.title_no_user));
if (TextUtils.isEmpty(password))
if (TextUtils.isEmpty(password) && !insecure)
throw new Throwable(getContext().getString(R.string.title_no_password)); throw new Throwable(getContext().getString(R.string.title_no_password));
CheckResult result = new CheckResult(); CheckResult result = new CheckResult();
result.folders = new ArrayList<>(); result.folders = new ArrayList<>();
// Check IMAP server / get folders // Check IMAP server / get folders
Properties props = MessageHelper.getSessionProperties(auth_type);
Properties props = MessageHelper.getSessionProperties(auth_type, insecure);
Session isession = Session.getInstance(props, null); Session isession = Session.getInstance(props, null);
isession.setDebug(true); isession.setDebug(true);
IMAPStore istore = null; IMAPStore istore = null;
try { try {
istore = (IMAPStore) isession.getStore("imaps");
istore = (IMAPStore) isession.getStore(starttls ? "imap" : "imaps");
try { try {
istore.connect(host, Integer.parseInt(port), user, password); istore.connect(host, Integer.parseInt(port), user, password);
} catch (AuthenticationFailedException ex) { } catch (AuthenticationFailedException ex) {
@ -574,6 +599,8 @@ public class FragmentAccount extends FragmentEx {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putLong("id", id); args.putLong("id", id);
args.putString("host", etHost.getText().toString()); args.putString("host", etHost.getText().toString());
args.putBoolean("starttls", cbStartTls.isChecked());
args.putBoolean("insecure", cbInsecure.isChecked());
args.putString("port", etPort.getText().toString()); args.putString("port", etPort.getText().toString());
args.putString("user", etUser.getText().toString()); args.putString("user", etUser.getText().toString());
args.putString("password", tilPassword.getEditText().getText().toString()); args.putString("password", tilPassword.getEditText().getText().toString());
@ -597,6 +624,8 @@ public class FragmentAccount extends FragmentEx {
@Override @Override
protected Void onLoad(Context context, Bundle args) throws Throwable { protected Void onLoad(Context context, Bundle args) throws Throwable {
String host = args.getString("host"); String host = args.getString("host");
boolean starttls = args.getBoolean("starttls");
boolean insecure = args.getBoolean("insecure");
String port = args.getString("port"); String port = args.getString("port");
String user = args.getString("user"); String user = args.getString("user");
String password = args.getString("password"); String password = args.getString("password");
@ -619,10 +648,10 @@ public class FragmentAccount extends FragmentEx {
if (TextUtils.isEmpty(host)) if (TextUtils.isEmpty(host))
throw new Throwable(getContext().getString(R.string.title_no_host)); throw new Throwable(getContext().getString(R.string.title_no_host));
if (TextUtils.isEmpty(port)) if (TextUtils.isEmpty(port))
port = "993";
port = (starttls ? "143" : "993");
if (TextUtils.isEmpty(user)) if (TextUtils.isEmpty(user))
throw new Throwable(getContext().getString(R.string.title_no_user)); throw new Throwable(getContext().getString(R.string.title_no_user));
if (TextUtils.isEmpty(password))
if (TextUtils.isEmpty(password) && !insecure)
throw new Throwable(getContext().getString(R.string.title_no_password)); throw new Throwable(getContext().getString(R.string.title_no_password));
if (TextUtils.isEmpty(interval)) if (TextUtils.isEmpty(interval))
interval = "9"; interval = "9";
@ -633,11 +662,11 @@ public class FragmentAccount extends FragmentEx {
// Check IMAP server // Check IMAP server
if (synchronize) { if (synchronize) {
Session isession = Session.getInstance(MessageHelper.getSessionProperties(auth_type), null);
Session isession = Session.getInstance(MessageHelper.getSessionProperties(auth_type, insecure), null);
isession.setDebug(true); isession.setDebug(true);
IMAPStore istore = null; IMAPStore istore = null;
try { try {
istore = (IMAPStore) isession.getStore("imaps");
istore = (IMAPStore) isession.getStore(starttls ? "imap" : "imaps");
try { try {
istore.connect(host, Integer.parseInt(port), user, password); istore.connect(host, Integer.parseInt(port), user, password);
} catch (AuthenticationFailedException ex) { } catch (AuthenticationFailedException ex) {
@ -669,6 +698,8 @@ public class FragmentAccount extends FragmentEx {
account = new EntityAccount(); account = new EntityAccount();
account.host = host; account.host = host;
account.starttls = starttls;
account.insecure = insecure;
account.port = Integer.parseInt(port); account.port = Integer.parseInt(port);
account.user = user; account.user = user;
account.password = password; account.password = password;
@ -828,6 +859,8 @@ public class FragmentAccount extends FragmentEx {
// Initialize // Initialize
Helper.setViewsEnabled(view, false); Helper.setViewsEnabled(view, false);
btnAuthorize.setVisibility(View.GONE); btnAuthorize.setVisibility(View.GONE);
cbStartTls.setVisibility(View.GONE);
cbInsecure.setVisibility(View.GONE);
tilPassword.setPasswordVisibilityToggleEnabled(id < 0); tilPassword.setPasswordVisibilityToggleEnabled(id < 0);
btnAdvanced.setVisibility(View.GONE); btnAdvanced.setVisibility(View.GONE);


+ 4
- 4
app/src/main/java/eu/faircode/email/FragmentFolder.java View File

@ -135,9 +135,9 @@ public class FragmentFolder extends FragmentEx {
if (folder == null || !folder.name.equals(name)) { if (folder == null || !folder.name.equals(name)) {
EntityAccount account = db.account().getAccount(folder == null ? aid : folder.account); EntityAccount account = db.account().getAccount(folder == null ? aid : folder.account);
Properties props = MessageHelper.getSessionProperties(account.auth_type);
Properties props = MessageHelper.getSessionProperties(account.auth_type, account.insecure);
Session isession = Session.getInstance(props, null); Session isession = Session.getInstance(props, null);
istore = (IMAPStore) isession.getStore("imaps");
istore = (IMAPStore) isession.getStore(account.starttls ? "imap" : "imaps");
Helper.connect(context, istore, account); Helper.connect(context, istore, account);
if (folder == null) { if (folder == null) {
@ -239,9 +239,9 @@ public class FragmentFolder extends FragmentEx {
EntityFolder folder = db.folder().getFolder(id); EntityFolder folder = db.folder().getFolder(id);
EntityAccount account = db.account().getAccount(folder.account); EntityAccount account = db.account().getAccount(folder.account);
Properties props = MessageHelper.getSessionProperties(account.auth_type);
Properties props = MessageHelper.getSessionProperties(account.auth_type, account.insecure);
Session isession = Session.getInstance(props, null); Session isession = Session.getInstance(props, null);
istore = (IMAPStore) isession.getStore("imaps");
istore = (IMAPStore) isession.getStore(account.starttls ? "imap" : "imaps");
Helper.connect(context, istore, account); Helper.connect(context, istore, account);
IMAPFolder ifolder = (IMAPFolder) istore.getFolder(folder.name); IMAPFolder ifolder = (IMAPFolder) istore.getFolder(folder.name);


+ 31
- 20
app/src/main/java/eu/faircode/email/FragmentIdentity.java View File

@ -21,8 +21,10 @@ package eu.faircode.email;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.preference.PreferenceManager;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -74,6 +76,7 @@ public class FragmentIdentity extends FragmentEx {
private Button btnAutoConfig; private Button btnAutoConfig;
private EditText etHost; private EditText etHost;
private CheckBox cbStartTls; private CheckBox cbStartTls;
private CheckBox cbInsecure;
private EditText etPort; private EditText etPort;
private EditText etUser; private EditText etUser;
private TextInputLayout tilPassword; private TextInputLayout tilPassword;
@ -102,6 +105,9 @@ public class FragmentIdentity extends FragmentEx {
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
setSubtitle(R.string.title_edit_identity); setSubtitle(R.string.title_edit_identity);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
final boolean insecure = prefs.getBoolean("insecure", false);
view = (ViewGroup) inflater.inflate(R.layout.fragment_identity, container, false); view = (ViewGroup) inflater.inflate(R.layout.fragment_identity, container, false);
// Get controls // Get controls
@ -120,8 +126,8 @@ public class FragmentIdentity extends FragmentEx {
etHost = view.findViewById(R.id.etHost); etHost = view.findViewById(R.id.etHost);
cbStartTls = view.findViewById(R.id.cbStartTls); cbStartTls = view.findViewById(R.id.cbStartTls);
cbInsecure = view.findViewById(R.id.cbInsecure);
etPort = view.findViewById(R.id.etPort); etPort = view.findViewById(R.id.etPort);
etUser = view.findViewById(R.id.etUser); etUser = view.findViewById(R.id.etUser);
tilPassword = view.findViewById(R.id.tilPassword); tilPassword = view.findViewById(R.id.tilPassword);
@ -191,21 +197,6 @@ public class FragmentIdentity extends FragmentEx {
} }
}); });
btnAdvanced.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int visibility = (grpAdvanced.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE);
grpAdvanced.setVisibility(visibility);
if (visibility == View.VISIBLE)
new Handler().post(new Runnable() {
@Override
public void run() {
((ScrollView) view).smoothScrollTo(0, tvEmail.getTop());
}
});
}
});
spProvider.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { spProvider.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override @Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) { public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
@ -275,6 +266,22 @@ public class FragmentIdentity extends FragmentEx {
} }
}); });
btnAdvanced.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int visibility = (grpAdvanced.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE);
grpAdvanced.setVisibility(visibility);
cbInsecure.setVisibility(insecure ? visibility : View.GONE);
if (visibility == View.VISIBLE)
new Handler().post(new Runnable() {
@Override
public void run() {
((ScrollView) view).smoothScrollTo(0, tvEmail.getTop());
}
});
}
});
cbStartTls.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { cbStartTls.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
@ -307,6 +314,7 @@ public class FragmentIdentity extends FragmentEx {
args.putInt("auth_type", account == null || account.auth_type == null ? Helper.AUTH_TYPE_PASSWORD : account.auth_type); args.putInt("auth_type", account == null || account.auth_type == null ? Helper.AUTH_TYPE_PASSWORD : account.auth_type);
args.putString("host", etHost.getText().toString()); args.putString("host", etHost.getText().toString());
args.putBoolean("starttls", cbStartTls.isChecked()); args.putBoolean("starttls", cbStartTls.isChecked());
args.putBoolean("insecure", cbInsecure.isChecked());
args.putString("port", etPort.getText().toString()); args.putString("port", etPort.getText().toString());
args.putString("user", etUser.getText().toString()); args.putString("user", etUser.getText().toString());
args.putString("password", tilPassword.getEditText().getText().toString()); args.putString("password", tilPassword.getEditText().getText().toString());
@ -324,6 +332,7 @@ public class FragmentIdentity extends FragmentEx {
String replyto = args.getString("replyto"); String replyto = args.getString("replyto");
String host = args.getString("host"); String host = args.getString("host");
boolean starttls = args.getBoolean("starttls"); boolean starttls = args.getBoolean("starttls");
boolean insecure = args.getBoolean("insecure");
String port = args.getString("port"); String port = args.getString("port");
String user = args.getString("user"); String user = args.getString("user");
String password = args.getString("password"); String password = args.getString("password");
@ -339,10 +348,10 @@ public class FragmentIdentity extends FragmentEx {
if (TextUtils.isEmpty(host)) if (TextUtils.isEmpty(host))
throw new IllegalArgumentException(getContext().getString(R.string.title_no_host)); throw new IllegalArgumentException(getContext().getString(R.string.title_no_host));
if (TextUtils.isEmpty(port)) if (TextUtils.isEmpty(port))
port = "465";
port = (starttls ? "587" : "465");
if (TextUtils.isEmpty(user)) if (TextUtils.isEmpty(user))
throw new IllegalArgumentException(getContext().getString(R.string.title_no_user)); throw new IllegalArgumentException(getContext().getString(R.string.title_no_user));
if (TextUtils.isEmpty(password))
if (TextUtils.isEmpty(password) && !insecure)
throw new IllegalArgumentException(getContext().getString(R.string.title_no_password)); throw new IllegalArgumentException(getContext().getString(R.string.title_no_password));
if (TextUtils.isEmpty(replyto)) if (TextUtils.isEmpty(replyto))
@ -350,7 +359,7 @@ public class FragmentIdentity extends FragmentEx {
// Check SMTP server // Check SMTP server
if (synchronize) { if (synchronize) {
Properties props = MessageHelper.getSessionProperties(auth_type);
Properties props = MessageHelper.getSessionProperties(auth_type, insecure);
Session isession = Session.getInstance(props, null); Session isession = Session.getInstance(props, null);
isession.setDebug(true); isession.setDebug(true);
Transport itransport = isession.getTransport(starttls ? "smtp" : "smtps"); Transport itransport = isession.getTransport(starttls ? "smtp" : "smtps");
@ -382,8 +391,9 @@ public class FragmentIdentity extends FragmentEx {
identity.email = email; identity.email = email;
identity.replyto = replyto; identity.replyto = replyto;
identity.host = host; identity.host = host;
identity.port = Integer.parseInt(port);
identity.starttls = starttls; identity.starttls = starttls;
identity.insecure = insecure;
identity.port = Integer.parseInt(port);
identity.user = user; identity.user = user;
identity.password = password; identity.password = password;
identity.auth_type = auth_type; identity.auth_type = auth_type;
@ -476,6 +486,7 @@ public class FragmentIdentity extends FragmentEx {
// Initialize // Initialize
Helper.setViewsEnabled(view, false); Helper.setViewsEnabled(view, false);
cbInsecure.setVisibility(View.GONE);
tilPassword.setPasswordVisibilityToggleEnabled(id < 0); tilPassword.setPasswordVisibilityToggleEnabled(id < 0);
btnSave.setVisibility(View.GONE); btnSave.setVisibility(View.GONE);
btnAdvanced.setVisibility(View.GONE); btnAdvanced.setVisibility(View.GONE);


+ 10
- 0
app/src/main/java/eu/faircode/email/FragmentOptions.java View File

@ -38,6 +38,7 @@ public class FragmentOptions extends FragmentEx {
private CheckBox cbBrowse; private CheckBox cbBrowse;
private CheckBox cbSwipe; private CheckBox cbSwipe;
private CheckBox cbCompact; private CheckBox cbCompact;
private CheckBox cbInsecure;
private CheckBox cbDebug; private CheckBox cbDebug;
@Override @Override
@ -54,6 +55,7 @@ public class FragmentOptions extends FragmentEx {
cbBrowse = view.findViewById(R.id.cbBrowse); cbBrowse = view.findViewById(R.id.cbBrowse);
cbSwipe = view.findViewById(R.id.cbSwipe); cbSwipe = view.findViewById(R.id.cbSwipe);
cbCompact = view.findViewById(R.id.cbCompact); cbCompact = view.findViewById(R.id.cbCompact);
cbInsecure = view.findViewById(R.id.cbInsecure);
cbDebug = view.findViewById(R.id.cbDebug); cbDebug = view.findViewById(R.id.cbDebug);
// Wire controls // Wire controls
@ -112,6 +114,14 @@ public class FragmentOptions extends FragmentEx {
} }
}); });
cbInsecure.setChecked(prefs.getBoolean("insecure", false));
cbInsecure.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
prefs.edit().putBoolean("insecure", checked).apply();
}
});
cbDebug.setChecked(prefs.getBoolean("debug", false)); cbDebug.setChecked(prefs.getBoolean("debug", false));
cbDebug.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { cbDebug.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override @Override


+ 28
- 12
app/src/main/java/eu/faircode/email/MessageHelper.java View File

@ -63,11 +63,13 @@ public class MessageHelper {
final static int NETWORK_TIMEOUT = 60 * 1000; // milliseconds final static int NETWORK_TIMEOUT = 60 * 1000; // milliseconds
static Properties getSessionProperties(int auth_type) {
static Properties getSessionProperties(int auth_type, boolean insecure) {
Properties props = new Properties(); Properties props = new Properties();
String checkserveridentity = Boolean.toString(!insecure).toLowerCase();
// https://javaee.github.io/javamail/docs/api/com/sun/mail/imap/package-summary.html#properties // https://javaee.github.io/javamail/docs/api/com/sun/mail/imap/package-summary.html#properties
props.put("mail.imaps.ssl.checkserveridentity", "true");
props.put("mail.imaps.ssl.checkserveridentity", checkserveridentity);
props.put("mail.imaps.ssl.trust", "*"); props.put("mail.imaps.ssl.trust", "*");
props.put("mail.imaps.starttls.enable", "false"); props.put("mail.imaps.starttls.enable", "false");
@ -79,22 +81,35 @@ public class MessageHelper {
props.put("mail.imaps.connectionpool.debug", "true"); props.put("mail.imaps.connectionpool.debug", "true");
props.put("mail.imaps.connectionpooltimeout", Integer.toString(3 * 60 * 1000)); // default: 45 sec props.put("mail.imaps.connectionpooltimeout", Integer.toString(3 * 60 * 1000)); // default: 45 sec
// "mail.imaps.finalizecleanclose"
// https://tools.ietf.org/html/rfc4978 // https://tools.ietf.org/html/rfc4978
// https://docs.oracle.com/javase/8/docs/api/java/util/zip/Deflater.html // https://docs.oracle.com/javase/8/docs/api/java/util/zip/Deflater.html
if (true) {
Log.i(Helper.TAG, "IMAP compress enabled");
props.put("mail.imaps.compress.enable", "true");
//props.put("mail.imaps.compress.level", "-1");
//props.put("mail.imaps.compress.strategy", "0");
}
props.put("mail.imaps.compress.enable", "true");
//props.put("mail.imaps.compress.level", "-1");
//props.put("mail.imaps.compress.strategy", "0");
props.put("mail.imaps.fetchsize", Integer.toString(48 * 1024)); // default 16K props.put("mail.imaps.fetchsize", Integer.toString(48 * 1024)); // default 16K
props.put("mail.imaps.peek", "true"); props.put("mail.imaps.peek", "true");
props.put("mail.imap.ssl.checkserveridentity", checkserveridentity);
props.put("mail.imap.ssl.trust", "*");
props.put("mail.imap.starttls.enable", "true");
props.put("mail.imap.starttls.required", "true");
props.put("mail.imap.auth", "true");
props.put("mail.imap.connectiontimeout", Integer.toString(NETWORK_TIMEOUT));
props.put("mail.imap.timeout", Integer.toString(NETWORK_TIMEOUT));
props.put("mail.imap.writetimeout", Integer.toString(NETWORK_TIMEOUT)); // one thread overhead
props.put("mail.imap.connectionpool.debug", "true");
props.put("mail.imap.connectionpooltimeout", Integer.toString(3 * 60 * 1000)); // default: 45 sec
props.put("mail.imap.compress.enable", "true");
props.put("mail.imap.fetchsize", Integer.toString(48 * 1024)); // default 16K
props.put("mail.imap.peek", "true");
// https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html#properties // https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html#properties
props.put("mail.smtps.ssl.checkserveridentity", "true");
props.put("mail.smtps.ssl.checkserveridentity", checkserveridentity);
props.put("mail.smtps.ssl.trust", "*"); props.put("mail.smtps.ssl.trust", "*");
props.put("mail.smtps.starttls.enable", "false"); props.put("mail.smtps.starttls.enable", "false");
props.put("mail.smtps.starttls.required", "false"); props.put("mail.smtps.starttls.required", "false");
@ -104,7 +119,7 @@ public class MessageHelper {
props.put("mail.smtps.writetimeout", Integer.toString(NETWORK_TIMEOUT)); // one thread overhead props.put("mail.smtps.writetimeout", Integer.toString(NETWORK_TIMEOUT)); // one thread overhead
props.put("mail.smtps.timeout", Integer.toString(NETWORK_TIMEOUT)); props.put("mail.smtps.timeout", Integer.toString(NETWORK_TIMEOUT));
props.put("mail.smtp.ssl.checkserveridentity", "true");
props.put("mail.smtp.ssl.checkserveridentity", checkserveridentity);
props.put("mail.smtp.ssl.trust", "*"); props.put("mail.smtp.ssl.trust", "*");
props.put("mail.smtp.starttls.enable", "true"); props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.starttls.required", "true"); props.put("mail.smtp.starttls.required", "true");
@ -145,6 +160,7 @@ public class MessageHelper {
Log.i(Helper.TAG, "Auth type=" + auth_type); Log.i(Helper.TAG, "Auth type=" + auth_type);
if (auth_type == Helper.AUTH_TYPE_GMAIL) { if (auth_type == Helper.AUTH_TYPE_GMAIL) {
props.put("mail.imaps.auth.mechanisms", "XOAUTH2"); props.put("mail.imaps.auth.mechanisms", "XOAUTH2");
props.put("mail.imap.auth.mechanisms", "XOAUTH2");
props.put("mail.smtps.auth.mechanisms", "XOAUTH2"); props.put("mail.smtps.auth.mechanisms", "XOAUTH2");
props.put("mail.smtp.auth.mechanisms", "XOAUTH2"); props.put("mail.smtp.auth.mechanisms", "XOAUTH2");
} }


+ 3
- 3
app/src/main/java/eu/faircode/email/ServiceSynchronize.java View File

@ -574,12 +574,12 @@ public class ServiceSynchronize extends LifecycleService {
System.setProperty("mail.socket.debug", Boolean.toString(debug)); System.setProperty("mail.socket.debug", Boolean.toString(debug));
// Create session // Create session
Properties props = MessageHelper.getSessionProperties(account.auth_type);
Properties props = MessageHelper.getSessionProperties(account.auth_type, account.insecure);
final Session isession = Session.getInstance(props, null); final Session isession = Session.getInstance(props, null);
isession.setDebug(debug); isession.setDebug(debug);
// adb -t 1 logcat | grep "fairemail\|System.out" // adb -t 1 logcat | grep "fairemail\|System.out"
final IMAPStore istore = (IMAPStore) isession.getStore("imaps");
final IMAPStore istore = (IMAPStore) isession.getStore(account.starttls ? "imap" : "imaps");
final Map<EntityFolder, IMAPFolder> folders = new HashMap<>(); final Map<EntityFolder, IMAPFolder> folders = new HashMap<>();
List<Thread> syncs = new ArrayList<>(); List<Thread> syncs = new ArrayList<>();
List<Thread> idlers = new ArrayList<>(); List<Thread> idlers = new ArrayList<>();
@ -1331,7 +1331,7 @@ public class ServiceSynchronize extends LifecycleService {
} }
// Create session // Create session
Properties props = MessageHelper.getSessionProperties(ident.auth_type);
Properties props = MessageHelper.getSessionProperties(ident.auth_type, ident.insecure);
final Session isession = Session.getInstance(props, null); final Session isession = Session.getInstance(props, null);
// Create message // Create message


+ 2
- 2
app/src/main/java/eu/faircode/email/ViewModelBrowse.java View File

@ -68,12 +68,12 @@ public class ViewModelBrowse extends ViewModel {
EntityAccount account = db.account().getAccount(folder.account); EntityAccount account = db.account().getAccount(folder.account);
if (imessages == null) { if (imessages == null) {
Properties props = MessageHelper.getSessionProperties(account.auth_type);
Properties props = MessageHelper.getSessionProperties(account.auth_type, account.insecure);
props.setProperty("mail.imap.throwsearchexception", "true"); props.setProperty("mail.imap.throwsearchexception", "true");
Session isession = Session.getInstance(props, null); Session isession = Session.getInstance(props, null);
Log.i(Helper.TAG, "Boundary connecting account=" + account.name); Log.i(Helper.TAG, "Boundary connecting account=" + account.name);
istore = (IMAPStore) isession.getStore("imaps");
istore = (IMAPStore) isession.getStore(account.starttls ? "imap" : "imaps");
Helper.connect(context, istore, account); Helper.connect(context, istore, account);
Log.i(Helper.TAG, "Boundary opening folder=" + folder.name); Log.i(Helper.TAG, "Boundary opening folder=" + folder.name);


+ 23
- 5
app/src/main/res/layout/fragment_account.xml View File

@ -58,8 +58,8 @@
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:minHeight="0dp"
android:minWidth="0dp" android:minWidth="0dp"
android:minHeight="0dp"
android:text="@string/title_autoconfig" android:text="@string/title_autoconfig"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etDomain" /> app:layout_constraintTop_toBottomOf="@id/etDomain" />
@ -118,6 +118,24 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvHost" /> app:layout_constraintTop_toBottomOf="@id/tvHost" />
<CheckBox
android:id="@+id/cbStartTls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_starttls"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etHost" />
<CheckBox
android:id="@+id/cbInsecure"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_allow_insecure"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbStartTls" />
<!-- port --> <!-- port -->
<TextView <TextView
@ -128,7 +146,7 @@
android:text="@string/title_port" android:text="@string/title_port"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etHost" />
app:layout_constraintTop_toBottomOf="@id/cbInsecure" />
<EditText <EditText
android:id="@+id/etPort" android:id="@+id/etPort"
@ -146,8 +164,8 @@
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:minHeight="0dp"
android:minWidth="0dp" android:minWidth="0dp"
android:minHeight="0dp"
android:text="@string/title_authorize" android:text="@string/title_authorize"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etPort" /> app:layout_constraintTop_toBottomOf="@id/etPort" />
@ -207,8 +225,8 @@
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:minHeight="0dp"
android:minWidth="0dp" android:minWidth="0dp"
android:minHeight="0dp"
android:text="@string/title_setup_advanced" android:text="@string/title_setup_advanced"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tilPassword" /> app:layout_constraintTop_toBottomOf="@id/tilPassword" />
@ -241,8 +259,8 @@
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:minHeight="0dp"
android:minWidth="0dp" android:minWidth="0dp"
android:minHeight="0dp"
android:text="@string/title_account_color" android:text="@string/title_account_color"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etName" /> app:layout_constraintTop_toBottomOf="@id/etName" />


+ 10
- 1
app/src/main/res/layout/fragment_identity.xml View File

@ -214,6 +214,15 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etHost" /> app:layout_constraintTop_toBottomOf="@id/etHost" />
<CheckBox
android:id="@+id/cbInsecure"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_allow_insecure"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbStartTls" />
<!-- port --> <!-- port -->
<TextView <TextView
@ -224,7 +233,7 @@
android:text="@string/title_port" android:text="@string/title_port"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbStartTls" />
app:layout_constraintTop_toBottomOf="@id/cbInsecure" />
<EditText <EditText
android:id="@+id/etPort" android:id="@+id/etPort"


+ 11
- 1
app/src/main/res/layout/fragment_options.xml View File

@ -71,6 +71,16 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbSwipe" /> app:layout_constraintTop_toBottomOf="@id/cbSwipe" />
<CheckBox
android:id="@+id/cbInsecure"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:text="@string/title_allow_insecure"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbCompact" />
<CheckBox <CheckBox
android:id="@+id/cbDebug" android:id="@+id/cbDebug"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -79,6 +89,6 @@
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:text="@string/title_advanced_debug" android:text="@string/title_advanced_debug"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbCompact" />
app:layout_constraintTop_toBottomOf="@id/cbInsecure" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView> </ScrollView>

+ 1
- 0
app/src/main/res/values/strings.xml View File

@ -106,6 +106,7 @@
<string name="title_custom">Custom</string> <string name="title_custom">Custom</string>
<string name="title_host">Host name</string> <string name="title_host">Host name</string>
<string name="title_starttls">STARTTLS</string> <string name="title_starttls">STARTTLS</string>
<string name="title_allow_insecure">Allow insecure connections</string>
<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>


Loading…
Cancel
Save