diff --git a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java index 8b9bb743e..b6a290916 100644 --- a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java +++ b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java @@ -387,9 +387,6 @@ public class FDroidApp extends Application implements androidx.work.Configuratio grantUriPermission(packageName, InstallHistoryService.LOG_URI, modeFlags); } - // find and process provisions if any. - Provisioner.scanAndProcess(getApplicationContext()); - // if the underlying OS version has changed, then fully rebuild the database SharedPreferences atStartTime = getAtStartTimeSharedPreferences(); if (Build.VERSION.SDK_INT != atStartTime.getInt("build-version", Build.VERSION.SDK_INT)) { diff --git a/app/src/main/java/org/fdroid/fdroid/Provisioner.java b/app/src/main/java/org/fdroid/fdroid/Provisioner.java deleted file mode 100644 index 809b3a298..000000000 --- a/app/src/main/java/org/fdroid/fdroid/Provisioner.java +++ /dev/null @@ -1,320 +0,0 @@ -package org.fdroid.fdroid; - -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.util.Base64; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.apache.commons.io.IOUtils; -import org.fdroid.fdroid.data.Repo; -import org.fdroid.fdroid.data.RepoProvider; -import org.fdroid.fdroid.views.ManageReposActivity; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -/** - * @author Michael Pöhn (michael.poehn@fsfe.org) - */ -@SuppressWarnings("LineLength") -public class Provisioner { - - public static final String TAG = "Provisioner"; - - /** - * This is the name of the subfolder in the file directory of this app - * where {@link Provisioner} looks for new provisions. - *

- * eg. in the Emulator (API level 24): /data/user/0/org.fdroid.fdroid.debug/files/provisions - */ - private static final String NEW_PROVISIONS_DIR = "provisions"; - - protected Provisioner() { - } - - /** - * search for provision files and process them - */ - static void scanAndProcess(Context context) { - File externalFilesDir = context.getExternalFilesDir(null); - if (externalFilesDir == null) { - return; - } - File provisionDir = new File(externalFilesDir.getAbsolutePath(), NEW_PROVISIONS_DIR); - - if (!provisionDir.isDirectory()) { - Utils.debugLog(TAG, "Provisions dir does not exists: '" + provisionDir.getAbsolutePath() + "' moving on ..."); - } else if (provisionDir.list().length == 0) { - Utils.debugLog(TAG, "Provisions dir is empty: '" + provisionDir.getAbsolutePath() + "' moving on ..."); - } else { - - Provisioner p = new Provisioner(); - List files = p.findProvisionFiles(context); - List plaintexts = p.extractProvisionsPlaintext(files); - List provisions = p.parseProvisions(plaintexts); - - if (provisions == null || provisions.isEmpty()) { - Utils.debugLog(TAG, "Provision dir is empty: '" + provisionDir.getAbsolutePath() + "' moving on ..."); - } else { - int cleanupCounter = 0; - for (Provision provision : provisions) { - if (provision.getRepositoryProvision() != null) { - RepositoryProvision repo = provision.getRepositoryProvision(); - - Repo storedRepo = RepoProvider.Helper.findByAddress(context, repo.getUrl()); - if (storedRepo != null) { - Utils.debugLog(TAG, "Provision contains a repo which is already added: '" + provision.getProvisonPath() + "' ignoring ..."); - } else { - // Note: only the last started activity will visible to users. - // All other prompting attempts will be lost. - Uri origUrl = Uri.parse(repo.getUrl()); - Uri.Builder data = new Uri.Builder(); - data.scheme(origUrl.getScheme()); - data.encodedAuthority(Uri.encode(repo.getUsername()) + ':' - + Uri.encode(repo.getPassword()) + '@' + Uri.encode(origUrl.getAuthority())); - data.path(origUrl.getPath()); - data.appendQueryParameter("fingerprint", repo.getSigfp()); - Intent i = new Intent(context, ManageReposActivity.class); - i.setData(data.build()); - context.startActivity(i); - Utils.debugLog(TAG, "Provision processed: '" - + provision.getProvisonPath() + "' prompted user ..."); - } - - } - - // remove provision file - try { - if (new File(provision.getProvisonPath()).delete()) { - cleanupCounter++; - } - } catch (SecurityException e) { - // ignore this exception - Utils.debugLog(TAG, "Removing provision not possible: " + e.getMessage() + " ()"); - } - } - Utils.debugLog(TAG, "Provisions done, removed " + cleanupCounter + " provision(s)."); - } - } - } - - private List findProvisionFiles(Context context) { - File externalFilesDir = context.getExternalFilesDir(null); - if (externalFilesDir == null) { - return Collections.emptyList(); - } - File provisionDir = new File(externalFilesDir.getAbsolutePath(), NEW_PROVISIONS_DIR); - return findProvisionFilesInDir(provisionDir); - } - - List findProvisionFilesInDir(File file) { - if (file == null || !file.isDirectory()) { - return Collections.emptyList(); - } - try { - File[] files = file.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - if (name != null && name.endsWith(".fdrp")) { - return true; - } - return false; - } - }); - return files != null ? Arrays.asList(files) : null; - } catch (Exception e) { - Utils.debugLog(TAG, "can not search for provisions, can not access: " + file.getAbsolutePath(), e); - return new ArrayList<>(); - } - } - - String rot13(String text) { - StringBuilder sb = new StringBuilder(text.length()); - for (int i = 0; i < text.length(); i++) { - char c = text.charAt(i); - if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M')) { - sb.append((char) (c + 13)); - } else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z')) { - sb.append((char) (c - 13)); - } else { - sb.append(c); - } - } - return sb.toString(); - } - - String deobfuscate(String obfuscated) { - try { - return new String(Base64.decode(rot13(obfuscated), Base64.DEFAULT), "UTF-8"); - } catch (UnsupportedEncodingException e) { - // encoding is defined to be utf8, continue gracefully if this magically fails. - return ""; - } - } - - List extractProvisionsPlaintext(List files) { - List result = new ArrayList<>(); - if (files != null) { - for (File file : files) { - ProvisionPlaintext plain = new ProvisionPlaintext(); - plain.setProvisionPath(file.getAbsolutePath()); - ZipInputStream in = null; - try { - in = new ZipInputStream(new FileInputStream(file)); - ZipEntry zipEntry; - while ((zipEntry = in.getNextEntry()) != null) { // NOPMD Avoid assignments in operands - String name = zipEntry.getName(); - if ("repo_provision.json".equals(name)) { - if (plain.getRepositoryProvision() != null) { - throw new IOException("provision malformed: contains more than one repo provision file."); - } - plain.setRepositoryProvision(IOUtils.toString(in, Charset.forName("UTF-8"))); - } else if ("repo_provision.ojson".equals(name)) { - if (plain.getRepositoryProvision() != null) { - throw new IOException("provision malformed: contains more than one repo provision file."); - } - plain.setRepositoryProvision(deobfuscate(IOUtils.toString(in, Charset.forName("UTF-8")))); - } - } - } catch (FileNotFoundException e) { - Utils.debugLog(TAG, String.format("finding provision '%s' failed", file.getPath()), e); - continue; - } catch (IOException e) { - Utils.debugLog(TAG, String.format("reading provision '%s' failed", file.getPath()), e); - continue; - } finally { - IOUtils.closeQuietly(in); - } - - result.add(plain); - } - } - return result; - } - - List parseProvisions(List provisionPlaintexts) { - - List provisions = new ArrayList<>(); - ObjectMapper mapper = new ObjectMapper(); - - if (provisionPlaintexts != null) { - for (ProvisionPlaintext provisionPlaintext : provisionPlaintexts) { - Provision provision = new Provision(); - provision.setProvisonPath(provisionPlaintext.getProvisionPath()); - try { - provision.setRepositoryProvision( - mapper.readValue(provisionPlaintext.getRepositoryProvision(), RepositoryProvision.class)); - provisions.add(provision); - } catch (IOException e) { - Utils.debugLog(TAG, "could not parse repository provision", e); - } - } - } - - return provisions; - } - - static class ProvisionPlaintext { - private String provisionPath; - private String repositoryProvision; - - String getProvisionPath() { - return provisionPath; - } - - void setProvisionPath(String provisionPath) { - this.provisionPath = provisionPath; - } - - String getRepositoryProvision() { - return repositoryProvision; - } - - void setRepositoryProvision(String repositoryProvision) { - this.repositoryProvision = repositoryProvision; - } - } - - static class Provision { - private String provisonPath; - private RepositoryProvision repositoryProvision; - - String getProvisonPath() { - return provisonPath; - } - - void setProvisonPath(String provisonPath) { - this.provisonPath = provisonPath; - } - - RepositoryProvision getRepositoryProvision() { - return repositoryProvision; - } - - void setRepositoryProvision(RepositoryProvision repositoryProvision) { - this.repositoryProvision = repositoryProvision; - } - } - - public static class RepositoryProvision { - - private String name; - private String url; - private String sigfp; - private String username; - private String password; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getSigfp() { - return sigfp; - } - - public void setSigfp(String sigfp) { - this.sigfp = sigfp; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - } -} diff --git a/app/src/test/java/org/fdroid/fdroid/ProvisionerTest.java b/app/src/test/java/org/fdroid/fdroid/ProvisionerTest.java deleted file mode 100644 index 3fd318007..000000000 --- a/app/src/test/java/org/fdroid/fdroid/ProvisionerTest.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.fdroid.fdroid; - -import org.fdroid.fdroid.shadows.ShadowLog; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; - -/** - * @author Michael Poehn (michael.poehn@fsfe.org) - */ -@RunWith(RobolectricTestRunner.class) -@SuppressWarnings("LineLength") -public class ProvisionerTest { - - @Before - public void setUp() { - ShadowLog.stream = System.out; - } - - @Test - public void provisionLookup() throws IOException { - // wired hack for getting resource dir path ... - String resourceDir = getResourceFile( - "demo_credentials_user1.fdrp").getParent(); - - Provisioner p = new Provisioner(); - List files = p.findProvisionFilesInDir(new File(resourceDir)); - - List expectedFilenames = Arrays.asList( - "demo_credentials_user1.fdrp", - "demo_credentials_user2.fdrp"); - - Assert.assertEquals(2, files.size()); - for (File f : files) { - Assert.assertTrue("unexpected file name " + f.getName(), expectedFilenames.contains(f.getName())); - } - } - - @Test - public void rot13() { - Provisioner p = new Provisioner(); - String result = p.rot13("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890{}\"':="); - Assert.assertEquals("nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM1234567890{}\"':=", result); - } - - @Test - public void deobfuscate() { - Provisioner p = new Provisioner(); - String result = p.deobfuscate("rlWVMKWuL2kcqUImVwbaGz90nTyhMlOyozE1pzImVTW1qPOwnTShM2HhWljXVPNtVPq3nTIhWmbtJlWuLz91qPN1ZQNtDv5QYvWqsD=="); - Assert.assertEquals("{\"Heraclitus\":'Nothing endures but change.',\n 'when': [\"about 500 B.C.\"]}", result); - } - - @Test - public void extractProvisionsPlaintextUnobfuscated() throws IOException { - Provisioner p = new Provisioner(); - List files = Arrays.asList(getResourceFile("demo_credentials_user2.fdrp")); - List result = p.extractProvisionsPlaintext(files); - - Assert.assertEquals(result.size(), 1); - Assert.assertEquals("{\"username\": \"user2\", \"password\": \"other secret\", \"name\": \"Example Repo\", \"url\": \"https://example.com/fdroid/repo\", \"sigfp\": \"1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff\"}", result.get(0).getRepositoryProvision()); - Assert.assertTrue(String.valueOf(result.get(0).getProvisionPath()).endsWith("demo_credentials_user2.fdrp")); - } - - @Test - public void extractProvisionsPlaintextObfuscated() throws IOException { - Provisioner p = new Provisioner(); - List files = Arrays.asList(getResourceFile("demo_credentials_user1.fdrp")); - List result = p.extractProvisionsPlaintext(files); - - Assert.assertEquals(result.size(), 1); - Assert.assertEquals("{\"sigfp\": \"1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff\", \"name\": \"Example Repo\", \"password\": \"secret1\", \"url\": \"https://example.com/fdroid/repo\", \"username\": \"user1\"}", result.get(0).getRepositoryProvision()); - Assert.assertTrue(String.valueOf(result.get(0).getProvisionPath()).endsWith("demo_credentials_user1.fdrp")); - } - - @Test - public void parseProvisions() { - - List plaintexts = Arrays.asList(new Provisioner.ProvisionPlaintext(), new Provisioner.ProvisionPlaintext()); - plaintexts.get(0).setProvisionPath("/some/dir/abc.fdrp"); - plaintexts.get(0).setRepositoryProvision("{\"username\": \"user1\", \"password\": \"secret1\", \"name\": \"test repo a\", \"url\": \"https://example.com/fdroid/repo\", \"sigfp\": \"1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff\"}"); - plaintexts.get(1).setProvisionPath("/some/dir/def.fdrp"); - plaintexts.get(1).setRepositoryProvision("{\"username\": \"user2\", \"name\": \"test repo a\", \"password\": \"other secret\", \"url\": \"https://example.com/fdroid/repo\", \"sigfp\": \"1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff\"}"); - - Provisioner p = new Provisioner(); - List result = p.parseProvisions(plaintexts); - - Assert.assertEquals("/some/dir/abc.fdrp", result.get(0).getProvisonPath()); - Assert.assertEquals("test repo a", result.get(0).getRepositoryProvision().getName()); - Assert.assertEquals("https://example.com/fdroid/repo", result.get(0).getRepositoryProvision().getUrl()); - Assert.assertEquals("1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff", result.get(0).getRepositoryProvision().getSigfp()); - Assert.assertEquals("user1", result.get(0).getRepositoryProvision().getUsername()); - Assert.assertEquals("secret1", result.get(0).getRepositoryProvision().getPassword()); - - Assert.assertEquals("/some/dir/def.fdrp", result.get(1).getProvisonPath()); - Assert.assertEquals("test repo a", result.get(1).getRepositoryProvision().getName()); - Assert.assertEquals("https://example.com/fdroid/repo", result.get(1).getRepositoryProvision().getUrl()); - Assert.assertEquals("1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff", result.get(1).getRepositoryProvision().getSigfp()); - Assert.assertEquals("user2", result.get(1).getRepositoryProvision().getUsername()); - Assert.assertEquals("other secret", result.get(1).getRepositoryProvision().getPassword()); - } - - private File getResourceFile(String resourceFileName) { - return new File(getClass().getClassLoader().getResource(resourceFileName).getPath()); - } -}