diff --git a/app/src/main/java/eu/faircode/email/ActivityError.java b/app/src/main/java/eu/faircode/email/ActivityError.java index 1dfde92629..b28fdd6c2c 100644 --- a/app/src/main/java/eu/faircode/email/ActivityError.java +++ b/app/src/main/java/eu/faircode/email/ActivityError.java @@ -19,14 +19,18 @@ package eu.faircode.email; Copyright 2018-2021 by Marcel Bokhorst (M66B) */ +import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.text.method.LinkMovementMethod; import android.view.LayoutInflater; import android.view.View; +import android.widget.Button; import android.widget.ImageButton; import android.widget.TextView; +import java.util.List; + public class ActivityError extends ActivityBase { static final int PI_ERROR = 1; static final int PI_ALERT = 2; @@ -42,6 +46,7 @@ public class ActivityError extends ActivityBase { TextView tvTitle = view.findViewById(R.id.tvTitle); TextView tvMessage = view.findViewById(R.id.tvMessage); + Button btnPassword = view.findViewById(R.id.btnPassword); ImageButton ibSetting = view.findViewById(R.id.ibSetting); ImageButton ibInfo = view.findViewById(R.id.ibInfo); @@ -49,20 +54,90 @@ public class ActivityError extends ActivityBase { String type = intent.getStringExtra("type"); String title = intent.getStringExtra("title"); String message = intent.getStringExtra("message"); + String provider = intent.getStringExtra("provider"); long account = intent.getLongExtra("account", -1L); + int protocol = intent.getIntExtra("protocol", -1); + int auth_type = intent.getIntExtra("auth_type", -1); int faq = intent.getIntExtra("faq", -1); tvTitle.setText(title); tvMessage.setMovementMethod(LinkMovementMethod.getInstance()); tvMessage.setText(message); + boolean outlook = (auth_type == ServiceAuthenticator.AUTH_TYPE_OAUTH && + ("office365".equals(provider) || "outlook".equals(provider))); + btnPassword.setVisibility(outlook ? View.VISIBLE : View.GONE); + btnPassword.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Bundle args = new Bundle(); + args.putLong("id", account); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) throws Throwable { + long id = args.getLong("id"); + + DB db = DB.getInstance(context); + try { + db.beginTransaction(); + + EntityAccount account = db.account().getAccount(id); + if (account == null) + return null; + + if (account.auth_type == ServiceAuthenticator.AUTH_TYPE_OAUTH && + ("office365".equals(account.provider) || + "outlook".equals(account.provider))) { + account.auth_type = ServiceAuthenticator.AUTH_TYPE_PASSWORD; + account.password = ""; + db.account().updateAccount(account); + + List identities = db.identity().getIdentities(account.id); + if (identities != null) + for (EntityIdentity identity : identities) + if (identity.auth_type == ServiceAuthenticator.AUTH_TYPE_OAUTH) { + identity.auth_type = ServiceAuthenticator.AUTH_TYPE_PASSWORD; + identity.password = ""; + db.identity().updateIdentity(identity); + } + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + return null; + } + + @Override + protected void onExecuted(Bundle args, Void data) { + startActivity(new Intent(ActivityError.this, ActivitySetup.class) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .putExtra("target", "accounts") + .putExtra("id", account) + .putExtra("protocol", protocol)); + finish(); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getSupportFragmentManager(), ex); + } + }.execute(ActivityError.this, args, "error:password"); + } + }); + ibSetting.setVisibility(account < 0 ? View.GONE : View.VISIBLE); ibSetting.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { v.getContext().startActivity(new Intent(v.getContext(), ActivitySetup.class) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .putExtra("target", "accounts")); + .putExtra("target", "accounts") + .putExtra("id", account) + .putExtra("protocol", protocol)); } }); diff --git a/app/src/main/java/eu/faircode/email/ActivitySetup.java b/app/src/main/java/eu/faircode/email/ActivitySetup.java index f2f210de53..a879e9ebdb 100644 --- a/app/src/main/java/eu/faircode/email/ActivitySetup.java +++ b/app/src/main/java/eu/faircode/email/ActivitySetup.java @@ -301,13 +301,18 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac if (getSupportFragmentManager().getFragments().size() == 0) { Intent intent = getIntent(); String target = intent.getStringExtra("target"); + long id = intent.getLongExtra("id", -1L); - FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); - if ("accounts".equals(target)) - fragmentTransaction.replace(R.id.content_frame, new FragmentAccounts()).addToBackStack("accounts"); - else - fragmentTransaction.replace(R.id.content_frame, new FragmentOptions()).addToBackStack("options"); - fragmentTransaction.commit(); + if ("accounts".equals(target) && id > 0) + onEditAccount(intent); + else { + FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); + if ("accounts".equals(target)) + fragmentTransaction.replace(R.id.content_frame, new FragmentAccounts()).addToBackStack("accounts"); + else + fragmentTransaction.replace(R.id.content_frame, new FragmentOptions()).addToBackStack("options"); + fragmentTransaction.commit(); + } if (intent.hasExtra("target")) { intent.removeExtra("target"); diff --git a/app/src/main/java/eu/faircode/email/Core.java b/app/src/main/java/eu/faircode/email/Core.java index e81897203a..43943b9e26 100644 --- a/app/src/main/java/eu/faircode/email/Core.java +++ b/app/src/main/java/eu/faircode/email/Core.java @@ -5118,10 +5118,12 @@ class Core { // Build pending intent Intent intent = new Intent(context, ActivityError.class); intent.setAction(channel + ":" + account.id + ":" + id); - intent.putExtra("type", channel); intent.putExtra("title", title); intent.putExtra("message", message); + intent.putExtra("provider", account.provider); intent.putExtra("account", account.id); + intent.putExtra("protocol", account.protocol); + intent.putExtra("auth_type", account.auth_type); intent.putExtra("faq", 22); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent pi = PendingIntentCompat.getActivity( diff --git a/app/src/main/java/eu/faircode/email/EmailService.java b/app/src/main/java/eu/faircode/email/EmailService.java index 85c8c6e4f3..fb413ee1bb 100644 --- a/app/src/main/java/eu/faircode/email/EmailService.java +++ b/app/src/main/java/eu/faircode/email/EmailService.java @@ -413,12 +413,12 @@ public class EmailService implements AutoCloseable { connect(host, port, auth, user, authenticator, factory); } catch (AuthenticationFailedException ex) { - if ("outlook.office365.com".equals(host) && - "AUTHENTICATE failed.".equals(ex.getMessage())) - throw new AuthenticationFailedException( - "The Outlook IMAP server is currently not accepting logins. " + - "Synchronizing and configuring accounts will work again after Microsoft has fixed this.", - ex.getNextException()); + //if ("outlook.office365.com".equals(host) && + // "AUTHENTICATE failed.".equals(ex.getMessage())) + // throw new AuthenticationFailedException( + // "The Outlook IMAP server is currently not accepting logins. " + + // "Synchronizing and configuring accounts will work again after Microsoft has fixed this.", + // ex.getNextException()); if (auth == AUTH_TYPE_GMAIL || auth == AUTH_TYPE_OAUTH) { try { diff --git a/app/src/main/java/eu/faircode/email/FragmentAccount.java b/app/src/main/java/eu/faircode/email/FragmentAccount.java index 5d1202b8e9..61a983283d 100644 --- a/app/src/main/java/eu/faircode/email/FragmentAccount.java +++ b/app/src/main/java/eu/faircode/email/FragmentAccount.java @@ -96,6 +96,7 @@ public class FragmentAccount extends FragmentBase { private EditText etPort; private EditText etUser; private TextInputLayout tilPassword; + private TextView tvAppPassword; private TextView tvPasswordStorage; private Button btnCertificate; private TextView tvCertificate; @@ -201,6 +202,7 @@ public class FragmentAccount extends FragmentBase { tvInsecureRemark = view.findViewById(R.id.tvInsecureRemark); etUser = view.findViewById(R.id.etUser); tilPassword = view.findViewById(R.id.tilPassword); + tvAppPassword = view.findViewById(R.id.tvAppPassword); tvPasswordStorage = view.findViewById(R.id.tvPasswordStorage); btnCertificate = view.findViewById(R.id.btnCertificate); tvCertificate = view.findViewById(R.id.tvCertificate); @@ -288,6 +290,9 @@ public class FragmentAccount extends FragmentBase { etUser.setTag(null); etUser.setText(null); tilPassword.getEditText().setText(null); + tvAppPassword.setVisibility( + "office365".equals(provider.id) || "outlook".equals(provider.id) + ? View.VISIBLE : View.GONE); certificate = null; tvCertificate.setText(R.string.title_optional); etRealm.setText(null); @@ -353,6 +358,15 @@ public class FragmentAccount extends FragmentBase { } }); + tvAppPassword.setVisibility(View.GONE); + tvAppPassword.setPaintFlags(tvAppPassword.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); + tvAppPassword.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Helper.viewFAQ(view.getContext(), 14); + } + }); + tvPasswordStorage.setPaintFlags(tvPasswordStorage.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); tvPasswordStorage.setOnClickListener(new View.OnClickListener() { @Override @@ -1454,6 +1468,9 @@ public class FragmentAccount extends FragmentBase { etUser.setText(account == null ? null : account.user); tilPassword.getEditText().setText(account == null ? null : account.password); + tvAppPassword.setVisibility(account != null && + ("office365".equals(account.provider) || "outlook".equals(account.provider)) + ? View.VISIBLE : View.GONE); certificate = (account == null ? null : account.certificate_alias); tvCertificate.setText(certificate == null ? getString(R.string.title_optional) : certificate); etRealm.setText(account == null ? null : account.realm); diff --git a/app/src/main/java/eu/faircode/email/FragmentFolders.java b/app/src/main/java/eu/faircode/email/FragmentFolders.java index fd3ec068cf..aaf77e5f4b 100644 --- a/app/src/main/java/eu/faircode/email/FragmentFolders.java +++ b/app/src/main/java/eu/faircode/email/FragmentFolders.java @@ -258,9 +258,42 @@ public class FragmentFolders extends FragmentBase { fabError.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - v.getContext().startActivity(new Intent(v.getContext(), ActivitySetup.class) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .putExtra("target", "accounts")); + Bundle args = new Bundle(); + args.putLong("id", account); + + new SimpleTask() { + @Override + protected EntityAccount onExecute(Context context, Bundle args) { + long id = args.getLong("id"); + + DB db = DB.getInstance(context); + return db.account().getAccount(id); + } + + @Override + protected void onExecuted(Bundle args, EntityAccount account) { + if (account == null) + return; + + String title = getString(R.string.title_notification_failed, account.name); + + Intent intent = new Intent(getContext(), ActivityError.class); + intent.putExtra("title", title); + intent.putExtra("message", account.error); + intent.putExtra("provider", account.provider); + intent.putExtra("account", account.id); + intent.putExtra("protocol", account.protocol); + intent.putExtra("auth_type", account.auth_type); + intent.putExtra("faq", 22); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + }.execute(FragmentFolders.this, args, "folders:error"); } }); diff --git a/app/src/main/java/eu/faircode/email/FragmentOAuth.java b/app/src/main/java/eu/faircode/email/FragmentOAuth.java index 54a3b0859b..334e553864 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOAuth.java +++ b/app/src/main/java/eu/faircode/email/FragmentOAuth.java @@ -511,6 +511,52 @@ public class FragmentOAuth extends FragmentBase { String iprotocol = (provider.smtp.starttls ? "smtp" : "smtps"); int iencryption = (provider.smtp.starttls ? EmailService.ENCRYPTION_STARTTLS : EmailService.ENCRYPTION_SSL); + if ("outlook".equals(id) && BuildConfig.DEBUG) { + DB db = DB.getInstance(context); + + // Create account + EntityAccount account = new EntityAccount(); + + account.host = provider.imap.host; + account.encryption = aencryption; + account.port = provider.imap.port; + account.auth_type = AUTH_TYPE_OAUTH; + account.provider = provider.id; + account.user = address; + account.password = state; + + int at = account.user.indexOf('@'); + String user = account.user.substring(0, at); + + account.name = provider.name + "/" + user; + + account.synchronize = true; + account.primary = false; + + if (provider.keepalive > 0) + account.poll_interval = provider.keepalive; + + account.partial_fetch = provider.partial; + + account.created = new Date().getTime(); + account.last_connected = account.created; + + account.id = db.account().insertAccount(account); + args.putLong("account", account.id); + EntityLog.log(context, "OAuth account=" + account.name); + + EntityFolder folder = new EntityFolder("INBOX", EntityFolder.INBOX); + folder.account = account.id; + folder.setProperties(); + folder.setSpecials(account); + folder.id = db.folder().insertFolder(folder); + EntityLog.log(context, "OAuth folder=" + folder.name + " type=" + folder.type); + if (folder.synchronize) + EntityOperation.sync(context, folder.id, true); + + return null; + } + /* * Outlook shared mailbox * Authenticate: main/shared account diff --git a/app/src/main/res/layout/activity_error.xml b/app/src/main/res/layout/activity_error.xml index ebc54ab464..7124b20cef 100644 --- a/app/src/main/res/layout/activity_error.xml +++ b/app/src/main/res/layout/activity_error.xml @@ -67,6 +67,19 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tvTitle" /> +