From e8b391a862468e066783f5a2e470225aa6396cfb Mon Sep 17 00:00:00 2001 From: M66B Date: Tue, 22 Mar 2022 10:45:02 +0100 Subject: [PATCH] Added importing provider profiles --- .../java/eu/faircode/email/ActivitySetup.java | 33 +++++ .../java/eu/faircode/email/ApplicationEx.java | 1 + .../java/eu/faircode/email/EmailProvider.java | 120 +++++++++++++----- .../faircode/email/FragmentOptionsMisc.java | 14 ++ .../main/res/layout/fragment_options_misc.xml | 12 +- app/src/main/res/values/strings.xml | 1 + 6 files changed, 150 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/eu/faircode/email/ActivitySetup.java b/app/src/main/java/eu/faircode/email/ActivitySetup.java index 98068dd820..3ebca349cf 100644 --- a/app/src/main/java/eu/faircode/email/ActivitySetup.java +++ b/app/src/main/java/eu/faircode/email/ActivitySetup.java @@ -142,6 +142,7 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac static final int REQUEST_OAUTH = 7; static final int REQUEST_STILL = 8; static final int REQUEST_DELETE_ACCOUNT = 9; + static final int REQUEST_IMPORT_PROVIDERS = 10; static final int PI_MISC = 1; @@ -462,6 +463,10 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac if (resultCode == RESULT_OK && data != null) handleImportCertificate(data); break; + case REQUEST_IMPORT_PROVIDERS: + if (resultCode == RESULT_OK && data != null) + handleImportProviders(data); + break; } } catch (Throwable ex) { Log.e(ex); @@ -1543,6 +1548,34 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac } } + private void handleImportProviders(Intent data) { + Bundle args = new Bundle(); + args.putParcelable("uri", data.getData()); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) throws Throwable { + Uri uri = args.getParcelable("uri"); + + Log.i("Reading URI=" + uri); + ContentResolver resolver = context.getContentResolver(); + EmailProvider.importProfiles(resolver.openInputStream(uri), context); + + return null; + } + + @Override + protected void onExecuted(Bundle args, Void data) { + ToastEx.makeText(ActivitySetup.this, R.string.title_completed, Toast.LENGTH_LONG).show(); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getSupportFragmentManager(), ex); + } + }.execute(this, args, "import:providers"); + } + private void onGmail(Intent intent) { FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.content_frame, new FragmentGmail()).addToBackStack("quick"); diff --git a/app/src/main/java/eu/faircode/email/ApplicationEx.java b/app/src/main/java/eu/faircode/email/ApplicationEx.java index c17bb83879..326dff8581 100644 --- a/app/src/main/java/eu/faircode/email/ApplicationEx.java +++ b/app/src/main/java/eu/faircode/email/ApplicationEx.java @@ -214,6 +214,7 @@ public class ApplicationEx extends Application Log.e(ex); } + EmailProvider.init(this); EncryptionHelper.init(this); MessageHelper.setSystemProperties(this); diff --git a/app/src/main/java/eu/faircode/email/EmailProvider.java b/app/src/main/java/eu/faircode/email/EmailProvider.java index 7b4ad27c15..789598d1a2 100644 --- a/app/src/main/java/eu/faircode/email/EmailProvider.java +++ b/app/src/main/java/eu/faircode/email/EmailProvider.java @@ -22,11 +22,11 @@ package eu.faircode.email; import static android.system.OsConstants.ECONNREFUSED; import android.content.Context; -import android.content.res.XmlResourceParser; import android.os.Parcel; import android.os.Parcelable; import android.system.ErrnoException; import android.text.TextUtils; +import android.util.Xml; import androidx.annotation.NonNull; @@ -37,9 +37,14 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; import java.net.ConnectException; import java.net.HttpURLConnection; import java.net.InetAddress; @@ -92,6 +97,10 @@ public class EmailProvider implements Parcelable { enum UserType {LOCAL, EMAIL, VALUE} + private static List imported; + private static final ExecutorService executor = + Helper.getBackgroundExecutor(0, "provider"); + private static final int SCAN_TIMEOUT = 15 * 1000; // milliseconds private static final int ISPDB_TIMEOUT = 15 * 1000; // milliseconds @@ -105,8 +114,6 @@ public class EmailProvider implements Parcelable { "keemail.me", // tutanota "ctemplar.com" )); - private static final ExecutorService executor = - Helper.getBackgroundExecutor(0, "provider"); private EmailProvider() { } @@ -115,6 +122,33 @@ public class EmailProvider implements Parcelable { this.name = name; } + static void init(Context context) { + executor.submit(new Runnable() { + @Override + public void run() { + File file = new File(context.getFilesDir(), "providers.xml"); + if (file.exists()) { + try (FileInputStream is = new FileInputStream(file)) { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(is, null); + imported = parseProfiles(parser); + } catch (Throwable ex) { + Log.e(ex); + } + } + } + }); + } + + static void importProfiles(InputStream is, Context context) throws IOException { + File file = new File(context.getFilesDir(), "providers.xml"); + try (OutputStream os = new FileOutputStream(file)) { + Helper.copy(is, os); + } + + init(context); + } + private void validate() throws UnknownHostException { if (TextUtils.isEmpty(this.imap.host) || this.imap.port <= 0 || TextUtils.isEmpty(this.smtp.host) || this.smtp.port <= 0) @@ -139,9 +173,38 @@ public class EmailProvider implements Parcelable { static List loadProfiles(Context context) { List result = null; + + try { + result = parseProfiles(context.getResources().getXml(R.xml.providers)); + } catch (Throwable ex) { + Log.e(ex); + } + + if (imported != null) + result.addAll(imported); + + final Collator collator = Collator.getInstance(Locale.getDefault()); + collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc + + Collections.sort(result, new Comparator() { + @Override + public int compare(EmailProvider p1, EmailProvider p2) { + int o = Integer.compare(p1.order, p2.order); + if (o == 0) + return collator.compare(p1.name, p2.name); + else + return o; + } + }); + + return result; + } + + private static List parseProfiles(XmlPullParser xml) { + List result = null; + try { EmailProvider provider = null; - XmlResourceParser xml = context.getResources().getXml(R.xml.providers); int eventType = xml.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_TAG) { @@ -156,7 +219,7 @@ public class EmailProvider implements Parcelable { provider.description = xml.getAttributeValue(null, "description"); if (provider.description == null) provider.description = provider.name; - provider.enabled = xml.getAttributeBooleanValue(null, "enabled", true); + provider.enabled = getAttributeBooleanValue(xml, "enabled", true); String domain = xml.getAttributeValue(null, "domain"); if (domain != null) @@ -166,11 +229,11 @@ public class EmailProvider implements Parcelable { if (mx != null) provider.mx = Arrays.asList(mx.split(",")); - provider.order = xml.getAttributeIntValue(null, "order", Integer.MAX_VALUE); - provider.keepalive = xml.getAttributeIntValue(null, "keepalive", 0); - provider.partial = xml.getAttributeBooleanValue(null, "partial", true); - provider.useip = xml.getAttributeBooleanValue(null, "useip", true); - provider.appPassword = xml.getAttributeBooleanValue(null, "appPassword", false); + provider.order = getAttributeIntValue(xml, "order", Integer.MAX_VALUE); + provider.keepalive = getAttributeIntValue(xml, "keepalive", 0); + provider.partial = getAttributeBooleanValue(xml, "partial", true); + provider.useip = getAttributeBooleanValue(xml, "useip", true); + provider.appPassword = getAttributeBooleanValue(xml, "appPassword", false); provider.link = xml.getAttributeValue(null, "link"); String documentation = xml.getAttributeValue(null, "documentation"); @@ -193,20 +256,20 @@ public class EmailProvider implements Parcelable { } else if ("imap".equals(name)) { provider.imap.score = 100; provider.imap.host = xml.getAttributeValue(null, "host"); - provider.imap.port = xml.getAttributeIntValue(null, "port", 0); - provider.imap.starttls = xml.getAttributeBooleanValue(null, "starttls", false); + provider.imap.port = getAttributeIntValue(xml, "port", 0); + provider.imap.starttls = getAttributeBooleanValue(xml, "starttls", false); } else if ("smtp".equals(name)) { provider.smtp.score = 100; provider.smtp.host = xml.getAttributeValue(null, "host"); - provider.smtp.port = xml.getAttributeIntValue(null, "port", 0); - provider.smtp.starttls = xml.getAttributeBooleanValue(null, "starttls", false); + provider.smtp.port = getAttributeIntValue(xml, "port", 0); + provider.smtp.starttls = getAttributeBooleanValue(xml, "starttls", false); } else if ("oauth".equals(name)) { provider.oauth = new OAuth(); - provider.oauth.enabled = xml.getAttributeBooleanValue(null, "enabled", false); - provider.oauth.askAccount = xml.getAttributeBooleanValue(null, "askAccount", false); + provider.oauth.enabled = getAttributeBooleanValue(xml, "enabled", false); + provider.oauth.askAccount = getAttributeBooleanValue(xml, "askAccount", false); provider.oauth.clientId = xml.getAttributeValue(null, "clientId"); provider.oauth.clientSecret = xml.getAttributeValue(null, "clientSecret"); - provider.oauth.pcke = xml.getAttributeBooleanValue(null, "pcke", false); + provider.oauth.pcke = getAttributeBooleanValue(xml, "pcke", false); provider.oauth.scopes = xml.getAttributeValue(null, "scopes").split(","); provider.oauth.authorizationEndpoint = xml.getAttributeValue(null, "authorizationEndpoint"); provider.oauth.tokenEndpoint = xml.getAttributeValue(null, "tokenEndpoint"); @@ -226,23 +289,20 @@ public class EmailProvider implements Parcelable { } catch (Throwable ex) { Log.e(ex); } - final Collator collator = Collator.getInstance(Locale.getDefault()); - collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc - - Collections.sort(result, new Comparator() { - @Override - public int compare(EmailProvider p1, EmailProvider p2) { - int o = Integer.compare(p1.order, p2.order); - if (o == 0) - return collator.compare(p1.name, p2.name); - else - return o; - } - }); return result; } + static boolean getAttributeBooleanValue(XmlPullParser parser, String name, boolean defaultValue) { + String value = parser.getAttributeValue(null, name); + return (value == null ? defaultValue : Boolean.parseBoolean(value)); + } + + static int getAttributeIntValue(XmlPullParser parser, String name, int defaultValue) { + String value = parser.getAttributeValue(null, name); + return (value == null ? defaultValue : Integer.parseInt(value)); + } + static EmailProvider getProvider(Context context, String id) throws FileNotFoundException { for (EmailProvider provider : loadProfiles(context)) if (id.equals(provider.id)) diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java b/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java index 74bc9f1eb8..430165313f 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java @@ -19,6 +19,7 @@ package eu.faircode.email; Copyright 2018-2022 by Marcel Bokhorst (M66B) */ +import android.app.Activity; import android.app.ActivityManager; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -172,6 +173,7 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc private SwitchCompat swInfra; private SwitchCompat swDupMsgId; private SwitchCompat swTestIab; + private Button btnImportProviders; private TextView tvProcessors; private TextView tvMemoryClass; private TextView tvMemoryUsage; @@ -333,6 +335,7 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc swInfra = view.findViewById(R.id.swInfra); swDupMsgId = view.findViewById(R.id.swDupMsgId); swTestIab = view.findViewById(R.id.swTestIab); + btnImportProviders = view.findViewById(R.id.btnImportProviders); tvProcessors = view.findViewById(R.id.tvProcessors); tvMemoryClass = view.findViewById(R.id.tvMemoryClass); tvMemoryUsage = view.findViewById(R.id.tvMemoryUsage); @@ -1144,6 +1147,17 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc } }); + btnImportProviders.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + Intent choose = Helper.getChooser(v.getContext(), intent); + getActivity().startActivityForResult(choose, ActivitySetup.REQUEST_IMPORT_PROVIDERS); + } + }); + btnGC.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/app/src/main/res/layout/fragment_options_misc.xml b/app/src/main/res/layout/fragment_options_misc.xml index e71be36e06..2a4399667e 100644 --- a/app/src/main/res/layout/fragment_options_misc.xml +++ b/app/src/main/res/layout/fragment_options_misc.xml @@ -1379,6 +1379,16 @@ app:layout_constraintTop_toBottomOf="@id/swDupMsgId" app:switchPadding="12dp" /> +