From 0ccc27d55c0976d530a158c67f7f3256f943cd75 Mon Sep 17 00:00:00 2001 From: M66B Date: Sun, 22 Jan 2023 12:43:33 +0100 Subject: [PATCH] Cloud sync: activate --- app/build.gradle | 4 ++ .../eu/faircode/email/ActivityCompose.java | 18 +++++++-- .../java/eu/faircode/email/CloudSync.java | 20 ++++++++-- .../faircode/email/FragmentOptionsBackup.java | 35 +++++++++++++++--- .../res/layout/fragment_options_backup.xml | 37 ++++++++++++++----- app/src/main/res/values/strings.xml | 4 ++ 6 files changed, 96 insertions(+), 22 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index cb5b07ac0e..b37a1db87e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -170,6 +170,7 @@ android { buildConfigField "String", "BITBUCKET_DOWNLOADS_URI", "\"https://bitbucket.org/M66B/fairemail-test/downloads/\"" buildConfigField "String", "ANNOUNCEMENT_URI", "\"https://gist.githubusercontent.com/M66B/d544192ca56224839d6ba0f2f6314c1f/raw/\"" buildConfigField "String", "CLOUD_URI", "\"https://api.fairemail.net/sync\"" + buildConfigField "String", "CLOUD_EMAIL", "\"cloud@in.faircode.eu\"" buildConfigField "String", "TX_URI", localProperties.getProperty("paypal.uri", "\"\"") buildConfigField "String", "GPA_URI", localProperties.getProperty("gpa.uri", "\"\"") buildConfigField "String", "INFO_URI", localProperties.getProperty("info.uri", "\"\"") @@ -191,6 +192,7 @@ android { buildConfigField "String", "BITBUCKET_DOWNLOADS_URI", "\"https://bitbucket.org/M66B/fairemail-test/downloads/\"" buildConfigField "String", "ANNOUNCEMENT_URI", "\"https://gist.githubusercontent.com/M66B/d544192ca56224839d6ba0f2f6314c1f/raw/\"" buildConfigField "String", "CLOUD_URI", "\"https://api.fairemail.net/sync\"" + buildConfigField "String", "CLOUD_EMAIL", "\"cloud@in.faircode.eu\"" buildConfigField "String", "TX_URI", "\"\"" buildConfigField "String", "GPA_URI", "\"\"" buildConfigField "String", "INFO_URI", "\"\"" @@ -213,6 +215,7 @@ android { buildConfigField "String", "BITBUCKET_DOWNLOADS_URI", "\"\"" buildConfigField "String", "ANNOUNCEMENT_URI", "\"\"" buildConfigField "String", "CLOUD_URI", "\"\"" + buildConfigField "String", "CLOUD_EMAIL", "\"\"" buildConfigField "String", "TX_URI", "\"\"" buildConfigField "String", "GPA_URI", "\"\"" buildConfigField "String", "INFO_URI", "\"\"" @@ -235,6 +238,7 @@ android { buildConfigField "String", "BITBUCKET_DOWNLOADS_URI", "\"\"" buildConfigField "String", "ANNOUNCEMENT_URI", "\"\"" buildConfigField "String", "CLOUD_URI", "\"\"" + buildConfigField "String", "CLOUD_EMAIL", "\"\"" buildConfigField "String", "TX_URI", "\"\"" buildConfigField "String", "GPA_URI", "\"\"" buildConfigField "String", "INFO_URI", "\"\"" diff --git a/app/src/main/java/eu/faircode/email/ActivityCompose.java b/app/src/main/java/eu/faircode/email/ActivityCompose.java index 3b92beb6fe..b5c08aadf1 100644 --- a/app/src/main/java/eu/faircode/email/ActivityCompose.java +++ b/app/src/main/java/eu/faircode/email/ActivityCompose.java @@ -71,9 +71,21 @@ public class ActivityCompose extends ActivityBase implements FragmentManager.OnB @Override public void onBackStackChanged() { if (getSupportFragmentManager().getBackStackEntryCount() == 0) { - String action = getIntent().getAction(); - if (!isShared(action) && - (action == null || !action.startsWith("widget:"))) { + Intent intent = getIntent(); + + String action = intent.getAction(); + boolean widget = (action != null && action.startsWith("widget:")); + + String[] tos = intent.getStringArrayExtra(Intent.EXTRA_EMAIL); + boolean cloud = (tos != null && tos.length == 1 && BuildConfig.CLOUD_EMAIL.equals(tos[0])); + + if (cloud) { + Intent setup = new Intent(this, ActivitySetup.class) + .setAction("misc") + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) + .putExtra("tab", "backup"); + startActivity(setup); + } else if (!isShared(action) && !widget) { Intent parent = getParentActivityIntent(); if (parent != null) if (shouldUpRecreateTask(parent)) diff --git a/app/src/main/java/eu/faircode/email/CloudSync.java b/app/src/main/java/eu/faircode/email/CloudSync.java index b4877266bb..9c1ae3dbf9 100644 --- a/app/src/main/java/eu/faircode/email/CloudSync.java +++ b/app/src/main/java/eu/faircode/email/CloudSync.java @@ -576,12 +576,24 @@ public class CloudSync { } } - private static JSONObject _call(Context context, String user, String password, JSONObject jrequest) - throws GeneralSecurityException, JSONException, IOException, InvalidCipherTextException { - byte[] salt = MessageDigest.getInstance("SHA256").digest(user.getBytes()); + private static byte[] getSalt(String user) throws NoSuchAlgorithmException { + return MessageDigest.getInstance("SHA256").digest(user.getBytes()); + } + + static String getCloudUser(String user) throws NoSuchAlgorithmException { + return getCloudUser(getSalt(user)); + } + + private static String getCloudUser(byte[] salt) throws NoSuchAlgorithmException { byte[] huser = MessageDigest.getInstance("SHA256").digest(salt); byte[] userid = Arrays.copyOfRange(huser, 0, 8); - String cloudUser = Base64.encodeToString(userid, Base64.NO_PADDING | Base64.NO_WRAP); + return Base64.encodeToString(userid, Base64.NO_PADDING | Base64.NO_WRAP); + } + + private static JSONObject _call(Context context, String user, String password, JSONObject jrequest) + throws GeneralSecurityException, JSONException, IOException, InvalidCipherTextException { + byte[] salt = getSalt(user); + String cloudUser = getCloudUser(salt); Pair key; String lookup = Helper.hex(salt) + ":" + password; diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsBackup.java b/app/src/main/java/eu/faircode/email/FragmentOptionsBackup.java index 348172a424..32f0c1156b 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsBackup.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsBackup.java @@ -128,6 +128,7 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere private Button btnLogout; private CheckBox cbDelete; private Group grpLogin; + private Group grpActivate; private Group grpLogout; private DateFormat DTF; @@ -171,6 +172,7 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere btnLogout = view.findViewById(R.id.btnLogout); cbDelete = view.findViewById(R.id.cbDelete); grpLogin = view.findViewById(R.id.grpLogin); + grpActivate = view.findViewById(R.id.grpActivate); grpLogout = view.findViewById(R.id.grpLogout); // Wire controls @@ -215,7 +217,18 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere btnActivate.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - // TODO + try { + String user = prefs.getString("cloud_user", null); + Intent intent = new Intent(Intent.ACTION_SEND) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .setType("text/plain") + .putExtra(Intent.EXTRA_EMAIL, new String[]{BuildConfig.CLOUD_EMAIL}) + .putExtra(Intent.EXTRA_SUBJECT, CloudSync.getCloudUser(user)) + .putExtra(Intent.EXTRA_TEXT, "Activate"); + v.getContext().startActivity(intent); + } catch (Throwable ex) { + Log.e(ex); + } } }); @@ -256,7 +269,6 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere !TextUtils.isEmpty(BuildConfig.CLOUD_URI) ? View.VISIBLE : View.GONE); Helper.linkPro(tvCloudPro); - btnActivate.setVisibility(View.GONE); cbSend.setChecked(prefs.getBoolean("cloud_send", true)); cbReceive.setChecked(prefs.getBoolean("cloud_receive", false)); @@ -278,10 +290,14 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere if (key == null || "cloud_user".equals(key) || "cloud_password".equals(key) || + "cloud_activated".equals(key) || + "cloud_busy".equals(key) || "cloud_last_sync".equals(key)) { String user = prefs.getString("cloud_user", null); String password = prefs.getString("cloud_password", null); boolean auth = !(TextUtils.isEmpty(user) || TextUtils.isEmpty(password)); + boolean activated = prefs.getBoolean("cloud_activated", false); + boolean busy = prefs.getBoolean("cloud_busy", false); long last_sync = prefs.getLong("cloud_last_sync", 0); etUser.setText(user); @@ -291,6 +307,7 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere last_sync == 0 ? "-" : DTF.format(last_sync))); cbDelete.setChecked(false); grpLogin.setVisibility(auth ? View.GONE : View.VISIBLE); + grpActivate.setVisibility(auth && !activated && !busy ? View.VISIBLE : View.GONE); grpLogout.setVisibility(auth ? View.VISIBLE : View.GONE); } } @@ -1522,15 +1539,19 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere } private void cloud(Bundle args) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + new SimpleTask() { @Override protected void onPreExecute(Bundle args) { Helper.setViewsEnabled(cardCloud, false); + prefs.edit().putBoolean("cloud_busy", true).apply(); } @Override protected void onPostExecute(Bundle args) { Helper.setViewsEnabled(cardCloud, true); + prefs.edit().putBoolean("cloud_busy", false).apply(); WorkerSync.init(getContext()); } @@ -1566,7 +1587,8 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere @Override protected void onExecuted(Bundle args, Void data) { - btnActivate.setVisibility(View.GONE); + prefs.edit().putBoolean("cloud_activated", true).apply(); + view.post(new Runnable() { @Override public void run() { @@ -1577,8 +1599,9 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere @Override protected void onException(Bundle args, Throwable ex) { - btnActivate.setVisibility(ex instanceof OperationCanceledException ? View.VISIBLE : View.GONE); - if (ex instanceof SecurityException) { + if (ex instanceof OperationCanceledException) + prefs.edit().putBoolean("cloud_activated", false).apply(); + else if (ex instanceof SecurityException) { AlertDialog.Builder builder = new AlertDialog.Builder(getContext()) .setIcon(R.drawable.twotone_warning_24) .setTitle(getString(R.string.title_advanced_cloud_invalid)) @@ -1587,7 +1610,7 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere if (!TextUtils.isEmpty(message)) builder.setMessage(message); builder.show(); - } else if (!(ex instanceof OperationCanceledException)) + } else Log.unexpectedError(getParentFragmentManager(), ex); } }.execute(FragmentOptionsBackup.this, args, "cloud"); diff --git a/app/src/main/res/layout/fragment_options_backup.xml b/app/src/main/res/layout/fragment_options_backup.xml index 6ea7fb5a84..ab4de06d17 100644 --- a/app/src/main/res/layout/fragment_options_backup.xml +++ b/app/src/main/res/layout/fragment_options_backup.xml @@ -324,11 +324,22 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/btnLogin" /> + +