diff --git a/F-Droid/res/values-ca/strings.xml b/F-Droid/res/values-ca/strings.xml index d909991ef..7851230ea 100644 --- a/F-Droid/res/values-ca/strings.xml +++ b/F-Droid/res/values-ca/strings.xml @@ -98,9 +98,6 @@ La voleu actualitzar? S\'ha actualitzat fa poc S\'està baixant %2$s / %3$s (%4$d%%) des de -%1$s - S\'està processant l\'aplicació -%2$d de %3$d des de %1$s S\'està connectant a %1$s diff --git a/F-Droid/res/values-de/strings.xml b/F-Droid/res/values-de/strings.xml index cfb2d0b9c..9d989fd67 100644 --- a/F-Droid/res/values-de/strings.xml +++ b/F-Droid/res/values-de/strings.xml @@ -171,9 +171,6 @@ Sollen diese aktualisiert werden? Konfigurieren Sie Ihren Proxy-Port (z.B. 8818) Herunterladen %2$s / %3$s (%4$d%%) von -%1$s - Anwendung wird vorbereitet -%2$d / %3$d von %1$s Mit %1$s wird verbunden diff --git a/F-Droid/res/values-el/strings.xml b/F-Droid/res/values-el/strings.xml index 13cbf27a0..1129a0120 100644 --- a/F-Droid/res/values-el/strings.xml +++ b/F-Droid/res/values-el/strings.xml @@ -123,9 +123,6 @@ Αναζήτηση για τοπικά αποθετήρια FDroid… Λήψη %2$s / %3$s (%4$d%%) από -%1$s - Επεξεργασία εφαρμογής -%2$d από %3$d από %1$s Σύνδεση με %1$s diff --git a/F-Droid/res/values-es/strings.xml b/F-Droid/res/values-es/strings.xml index 84650bdb7..b40c5be8f 100644 --- a/F-Droid/res/values-es/strings.xml +++ b/F-Droid/res/values-es/strings.xml @@ -170,9 +170,6 @@ La dirección de un repositorio es algo similar a esto: https://f-droid.org/repo Configurar el número del puerto del proxy (p.ej. 8118) Descargando %2$s / %3$s (%4$d%%) de -%1$s - Procesando la aplicación -%2$d de %3$d desde %1$s Conectando a %1$s diff --git a/F-Droid/res/values-fa/strings.xml b/F-Droid/res/values-fa/strings.xml index 0546cb911..4474ab384 100644 --- a/F-Droid/res/values-fa/strings.xml +++ b/F-Droid/res/values-fa/strings.xml @@ -82,9 +82,6 @@ همه دریافت %2$s / %3$s (%4$d%%) از -%1$s - پردازش برنامه -%2$d از %3$d از %1$s اتصال به %1$s diff --git a/F-Droid/res/values-fr/strings.xml b/F-Droid/res/values-fr/strings.xml index 40b29b615..ae83bc254 100644 --- a/F-Droid/res/values-fr/strings.xml +++ b/F-Droid/res/values-fr/strings.xml @@ -167,9 +167,6 @@ Voulez-vous les mettre à jour? Configurer votre numéro de port de proxy (ex. 8118) Téléchargement %2$s / %3$s (%4$d%%) de -%1$s - Prise en compte de l\'application -%2$d de %3$d depuis %1$s Connexion à %1$s diff --git a/F-Droid/res/values-gl/strings.xml b/F-Droid/res/values-gl/strings.xml index 0175f1829..c4e3032ea 100644 --- a/F-Droid/res/values-gl/strings.xml +++ b/F-Droid/res/values-gl/strings.xml @@ -95,9 +95,6 @@ Quere actualizalos? Actualizado recentemente Descargando %2$s / %3$s (%4$d%%) desde -%1$s - Procesando o aplicativo -%2$d de %3$d desde %1$s Conectándose con %1$s diff --git a/F-Droid/res/values-hu/strings.xml b/F-Droid/res/values-hu/strings.xml index 8b5f3c9d5..59f1f864b 100644 --- a/F-Droid/res/values-hu/strings.xml +++ b/F-Droid/res/values-hu/strings.xml @@ -113,9 +113,6 @@ Szeretné ezeket frissíteni?\" Legutóbb frissítve \"Letöltés %2$s / %3$s (%4$d%%) innen -%1$s\" - \"Feldolgozási kérelem -%2$d of %3$d innen %1$s\" \"Kapcsolódás %1$s\" diff --git a/F-Droid/res/values-it/strings.xml b/F-Droid/res/values-it/strings.xml index 758a24f8d..cb3aadad0 100644 --- a/F-Droid/res/values-it/strings.xml +++ b/F-Droid/res/values-it/strings.xml @@ -170,9 +170,6 @@ Vuoi aggiornarlo? Configura la porta del tuo proxy (es. 8118) Scaricamento %2$s / %3$s (%4$d%%) da -%1$s - Elaborazione applicazione -%2$d di %3$d da %1$s Connessione a %1$s diff --git a/F-Droid/res/values-ja/strings.xml b/F-Droid/res/values-ja/strings.xml index db1ea8799..c6e89b9da 100644 --- a/F-Droid/res/values-ja/strings.xml +++ b/F-Droid/res/values-ja/strings.xml @@ -172,9 +172,6 @@ GNU GPLv3 ライセンスに基づいてリリースされました. プロキシーのポート番号を設定(例:8118) ダウンロード中 %2$s / %3$s (%4$d%%) ダウンロード元 -%1$s - アプリケーションの処理中 -%2$d / %3$d %1$s 接続中 %1$s diff --git a/F-Droid/res/values-ko/strings.xml b/F-Droid/res/values-ko/strings.xml index 5baeb291d..8b3eba2d6 100644 --- a/F-Droid/res/values-ko/strings.xml +++ b/F-Droid/res/values-ko/strings.xml @@ -78,9 +78,6 @@ 최근 업데이트 %1$s 에서 다운로드 중입니다. %2$s / %3$s (%4$d%%) - 응용 프로그램 처리중 -%1$s -%2$d / %3$d %1$s에 접속중 장치와 응용프로그램의 호환성 확인중… 사용된 권한이 없습니다. diff --git a/F-Droid/res/values-nb/strings.xml b/F-Droid/res/values-nb/strings.xml index 6ac15c73d..1559ebb84 100644 --- a/F-Droid/res/values-nb/strings.xml +++ b/F-Droid/res/values-nb/strings.xml @@ -165,14 +165,8 @@ Lisensiert GNU GPLv3. Sett opp tjenernavn for din mellomtjener (f.eks. 127.0.0.1) Mellomtjener-port Sett opp portnummer for din mellomtjener (f.eks. 8118) - Laster ned -%2$s / %3$s (%4$d%%) fra -%1$s - Prosesserer applikasjon -%2$d of %3$d fra -%1$s - Kobler til -%1$s + Laster ned\n%2$s / %3$s (%4$d%%) fra\n%1$s + Kobler til\n%1$s Sjekker programstøtte for ditt utstyr… Lagrer programdata (%1$d%%) Ingen av pakkebrønnene hadde noen oppdateringer diff --git a/F-Droid/res/values-nl/strings.xml b/F-Droid/res/values-nl/strings.xml index 2be53666c..495cb9a79 100644 --- a/F-Droid/res/values-nl/strings.xml +++ b/F-Droid/res/values-nl/strings.xml @@ -124,9 +124,6 @@ Wilt u ze vernieuwen? Lokale FDroid opslagplaatsen Downloaden %2$s / %3$s (%4$d%%) van -%1$s - Verwerken applicatie -%2$d van %3$d van %1$s Verbinden met %1$s Controleer app compatibiliteit met uw apparaat… diff --git a/F-Droid/res/values-pl/strings.xml b/F-Droid/res/values-pl/strings.xml index fca30577e..eee271076 100644 --- a/F-Droid/res/values-pl/strings.xml +++ b/F-Droid/res/values-pl/strings.xml @@ -129,7 +129,6 @@ Czy chcesz je zaktualizować? Skonfiguruj host proxy Port proxy Skonfiguruj port proxy - Przetwarzanie aplikacji %2$d / %3$d z %1$s Trwa łączenie z %1$s Sprawdzanie kompatybilności aplikacji z urządzeniem… diff --git a/F-Droid/res/values-pt-rBR/strings.xml b/F-Droid/res/values-pt-rBR/strings.xml index 282342faf..9548ea21a 100644 --- a/F-Droid/res/values-pt-rBR/strings.xml +++ b/F-Droid/res/values-pt-rBR/strings.xml @@ -138,9 +138,6 @@ Você deseja atualizá-los? Configurar o número da porta do seu proxy (ex. 8118) Baixando %2$s / %3$s (%4$d%%) de -%1$s - Processando aplicativo -%2$d de %3$d, de %1$s Conectando-se a %1$s diff --git a/F-Droid/res/values-ru/strings.xml b/F-Droid/res/values-ru/strings.xml index 955d42a06..6487c539a 100644 --- a/F-Droid/res/values-ru/strings.xml +++ b/F-Droid/res/values-ru/strings.xml @@ -171,9 +171,6 @@ Настройка номера порта вашего прокси (напр. 8118) Загрузка %2$s / %3$s (%4$d%%) из -%1$s - Обработка приложения -%2$d из %3$d от %1$s Соединение с %1$s diff --git a/F-Droid/res/values-sr/strings.xml b/F-Droid/res/values-sr/strings.xml index 038a8d28d..c8b85e627 100644 --- a/F-Droid/res/values-sr/strings.xml +++ b/F-Droid/res/values-sr/strings.xml @@ -169,9 +169,6 @@ Подесите порт вашег проксија (нпр. 8118) Преузимам %2$s / %3$s (%4$d%%) са -%1$s - Обрађујем апликацију -%2$d од %3$d са %1$s Повезујем се са %1$s diff --git a/F-Droid/res/values-sv/strings.xml b/F-Droid/res/values-sv/strings.xml index 15d0ec554..9e71591b0 100644 --- a/F-Droid/res/values-sv/strings.xml +++ b/F-Droid/res/values-sv/strings.xml @@ -170,9 +170,6 @@ Vill du uppdatera dem? Konfigurera din proxys portnummer (t.ex. 8118) Hämtar %2$s / %3$s (%4$d%%) från -%1$s - Bearbetar program -%2$d av %3$d från %1$s Ansluter till %1$s diff --git a/F-Droid/res/values-tr/strings.xml b/F-Droid/res/values-tr/strings.xml index d79101f59..aebaa0d88 100644 --- a/F-Droid/res/values-tr/strings.xml +++ b/F-Droid/res/values-tr/strings.xml @@ -153,9 +153,6 @@ Güncellemek ister misiniz? Proxy port numarasını yapılandır (örn. 8118) İndiriliyor %2$s / %3$s (%4$d%%) şuradan -%1$s - Uygulama ele alınıyor -%2$d toplam %3$d şuradan %1$s %1$s konumuna bağlanılıyor diff --git a/F-Droid/res/values-ug/strings.xml b/F-Droid/res/values-ug/strings.xml index 0b1921925..0cf70f83d 100644 --- a/F-Droid/res/values-ug/strings.xml +++ b/F-Droid/res/values-ug/strings.xml @@ -91,9 +91,6 @@ يېقىنقى يېڭىلانغانلار چۈشۈرۈۋاتىدۇ %2$s / %3$s (%4$d%%) -%1$s - ئەپنى بىر تەرەپ قىلىۋاتىدۇ -%2$d of %3$d %1$s %1$s غا باغلىنىۋاتىدۇ diff --git a/F-Droid/res/values-zh-rCN/strings.xml b/F-Droid/res/values-zh-rCN/strings.xml index be9385e8a..d9cd34c5f 100644 --- a/F-Droid/res/values-zh-rCN/strings.xml +++ b/F-Droid/res/values-zh-rCN/strings.xml @@ -140,9 +140,6 @@ https://f-droid.org/repo 正在从以下位置下载: %1$s 进度:%2$s / %3$s (%4$d%%) - 正在处理以下位置的应用程序: -%1$s -进度:%2$d / %3$d 正在连接到 %1$s 正在检查应用程序与您的设备的兼容性… diff --git a/F-Droid/res/values/strings.xml b/F-Droid/res/values/strings.xml index bfc956e78..f6e316951 100644 --- a/F-Droid/res/values/strings.xml +++ b/F-Droid/res/values/strings.xml @@ -211,7 +211,7 @@ - Percentage complete (int between 0-100) --> Downloading\n%2$s / %3$s (%4$d%%) from\n%1$s - Processing application\n%2$d of %3$d from\n%1$s + Processing %2$s / %3$s (%4$d%%) from %1$s Connecting to\n%1$s Checking apps compatibility with your device… Saving application details (%1$d%%) diff --git a/F-Droid/src/org/fdroid/fdroid/ProgressBufferedInputStream.java b/F-Droid/src/org/fdroid/fdroid/ProgressBufferedInputStream.java new file mode 100644 index 000000000..526d91105 --- /dev/null +++ b/F-Droid/src/org/fdroid/fdroid/ProgressBufferedInputStream.java @@ -0,0 +1,51 @@ +package org.fdroid.fdroid; + +import android.os.Bundle; + +import org.fdroid.fdroid.data.Repo; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProgressBufferedInputStream extends BufferedInputStream { + private static final String TAG = "ProgressBufferedInputSt"; + + final Repo repo; + final ProgressListener progressListener; + final Bundle data; + final int totalBytes; + + int currentBytes = 0; + + /** + * Reports progress to the specified {@link ProgressListener}, with the + * progress based on the {@code totalBytes}. + */ + public ProgressBufferedInputStream(InputStream in, ProgressListener progressListener, Repo repo, int totalBytes) + throws IOException { + super(in); + this.progressListener = progressListener; + this.repo = repo; + this.data = new Bundle(1); + this.data.putString(RepoUpdater.PROGRESS_DATA_REPO_ADDRESS, repo.address); + this.totalBytes = totalBytes; + } + + @Override + public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { + if (progressListener != null) { + currentBytes += byteCount; + /* don't send every change to keep things efficient. 333333 bytes to keep all + * the digits changing because it looks pretty, < 9000 since the reads won't + * line up exactly */ + if (currentBytes % 333333 < 9000) { + progressListener.onProgress( + new ProgressListener.Event( + RepoUpdater.PROGRESS_TYPE_PROCESS_XML, + currentBytes, totalBytes, data)); + } + } + return super.read(buffer, byteOffset, byteCount); + } +} diff --git a/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java b/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java index 599ca7a41..f3cff0b8b 100644 --- a/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java +++ b/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java @@ -19,14 +19,12 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; -import java.io.BufferedReader; import java.io.File; -import java.io.FileOutputStream; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; +import java.security.CodeSigner; import java.security.cert.Certificate; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -49,13 +47,18 @@ public class RepoUpdater { private List apps = new ArrayList<>(); private List apks = new ArrayList<>(); private RepoUpdateRememberer rememberer = null; - protected boolean usePubkeyInJar = false; protected boolean hasChanged = false; @Nullable protected ProgressListener progressListener; - public RepoUpdater(@NonNull Context ctx, @NonNull Repo repo) { - this.context = ctx; - this.repo = repo; + /** + * Updates an app repo as read out of the database into a {@link Repo} instance. + * + * @param context + * @param repo a {@link Repo} read out of the local database + */ + public RepoUpdater(@NonNull Context context, @NonNull Repo repo) { + this.context = context; + this.repo = repo; } public void setProgressListener(ProgressListener progressListener) { @@ -68,39 +71,6 @@ public class RepoUpdater { public List getApks() { return apks; } - /** - * All repos are represented by a signed jar file, {@code index.jar}, which contains - * a single file, {@code index.xml}. This takes the {@code index.jar}, verifies the - * signature, then returns the unzipped {@code index.xml}. - * - * @throws UpdateException All error states will come from here. - */ - protected File getIndexFromFile(File downloadedFile) throws UpdateException { - final Date updateTime = new Date(System.currentTimeMillis()); - Log.d(TAG, "Getting signed index from " + repo.address + " at " + - Utils.formatLogDate(updateTime)); - - final File indexJar = downloadedFile; - File indexXml = null; - - // Don't worry about checking the status code for 200. If it was a - // successful download, then we will have a file ready to use: - if (indexJar != null && indexJar.exists()) { - - // Due to a bug in android 5.0 lollipop, the inclusion of BouncyCastle causes - // breakage when verifying the signature of the downloaded .jar. For more - // details, check out https://gitlab.com/fdroid/fdroidclient/issues/111. - try { - FDroidApp.disableSpongyCastleOnLollipop(); - indexXml = extractIndexFromJar(indexJar); - } finally { - FDroidApp.enableSpongyCastleOnLollipop(); - } - - } - return indexXml; - } - protected String getIndexAddress() { try { String versionName = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName; @@ -111,7 +81,7 @@ public class RepoUpdater { return repo.address + "/index.jar"; } - protected Downloader downloadIndex() throws UpdateException { + Downloader downloadIndex() throws UpdateException { Downloader downloader = null; try { downloader = DownloaderFactory.create( @@ -141,77 +111,83 @@ public class RepoUpdater { return downloader; } - private int estimateAppCount(File indexFile) { - int count = -1; - try { - // A bit of a hack, this might return false positives if an apps description - // or some other part of the XML file contains this, but it is a pretty good - // estimate and makes the progress counter more informative. - // As with asking the server about the size of the index before downloading, - // this also has a time tradeoff. It takes about three seconds to iterate - // through the file and count 600 apps on a slow emulator (v17), but if it is - // taking two minutes to update, the three second wait may be worth it. - final String APPLICATION = " 1) { + throw new UpdateException(repo, "index.jar must be signed by a single code signer!"); } - return match; - } - - protected File extractIndexFromJar(File indexJar) throws UpdateException { - File indexFile = null; - JarFile jarFile = null; - try { - jarFile = new JarFile(indexJar, true); - JarEntry indexEntry = (JarEntry) jarFile.getEntry("index.xml"); - - indexFile = File.createTempFile("index-", "-extracted.xml", context.getCacheDir()); - InputStream input = null; - OutputStream output = null; - try { - /* - * JarFile.getInputStream() provides the signature check, even - * though the Android docs do not mention this, the Java docs do - * and Android seems to implement it the same: - * http://docs.oracle.com/javase/6/docs/api/java/util/jar/JarFile.html#getInputStream(java.util.zip.ZipEntry) - * https://developer.android.com/reference/java/util/jar/JarFile.html#getInputStream(java.util.zip.ZipEntry) - */ - input = jarFile.getInputStream(indexEntry); - output = new FileOutputStream(indexFile); - Utils.copy(input, output); - } finally { - Utils.closeQuietly(output); - Utils.closeQuietly(input); - } - - // Can only read certificates from jar after it has been read - // completely, so we put it after the copy above... - if (isTofuRequest()) { - Log.i(TAG, "Implicitly trusting the signature of index.jar, because this is a TOFU request"); - // Note that later on in the process we will save the pubkey against they repo, so - // that future requests verify against the signature we got this time. - } else if (!verifyCerts(indexEntry)) { - indexFile.delete(); - throw new UpdateException(repo, "Index signature mismatch"); - } - } catch (IOException e) { - if (indexFile != null) { - indexFile.delete(); - } - throw new UpdateException( - repo, "Error opening signed index", e); - } finally { - if (jarFile != null) { - try { - jarFile.close(); - } catch (IOException ioe) { - // ignore - } - } + List certs = codeSigners[0].getSignerCertPath().getCertificates(); + if (certs.size() != 1) { + throw new UpdateException(repo, "index.jar code signers must only have a single certificate!"); } - - return indexFile; + return (X509Certificate) certs.get(0); } /** - * If the repo doesn't have a fingerprint, then this is a "Trust On First Use" (TOFU) - * request. In that case, we will not verify the certificate, but rather implicitly trust - * the file we downloaded. We'll extract the certificate from the jar, and then use that - * to verify future requests to the same repository. + * A new repo can be added with or without the fingerprint of the signing + * certificate. If no fingerprint is supplied, then do a pure TOFU and just + * store the certificate as valid. If there is a fingerprint, then first + * check that the signing certificate in the jar matches that fingerprint. */ - private boolean isTofuRequest() { - return TextUtils.isEmpty(repo.fingerprint); + private void verifyAndStoreTOFUCerts(String certFromIndexXml, X509Certificate rawCertFromJar) + throws UpdateException { + if (repo.pubkey != null) + return; // there is a repo.pubkey already, nothing to TOFU + + /* The first time a repo is added, it can be added with the signing certificate's + * fingerprint. In that case, check that fingerprint against what is + * actually in the index.jar itself. If no fingerprint, just store the + * signing certificate */ + boolean trustNewSigningCertificate = false; + if (repo.fingerprint == null) { + // no info to check things are valid, so just Trust On First Use + trustNewSigningCertificate = true; + } else { + String fingerprintFromIndexXml = Utils.calcFingerprint(certFromIndexXml); + String fingerprintFromJar = Utils.calcFingerprint(rawCertFromJar); + if (repo.fingerprint.equalsIgnoreCase(fingerprintFromIndexXml) + && repo.fingerprint.equalsIgnoreCase(fingerprintFromJar)) { + trustNewSigningCertificate = true; + } else { + throw new UpdateException(repo, "Supplied certificate fingerprint does not match!"); + } + } + + if (trustNewSigningCertificate) { + Log.d(TAG, "Saving new signing certificate in the database for " + repo.address); + ContentValues values = new ContentValues(2); + values.put(RepoProvider.DataColumns.LAST_UPDATED, Utils.formatDate(new Date(), "")); + values.put(RepoProvider.DataColumns.PUBLIC_KEY, Hasher.hex(rawCertFromJar)); + RepoProvider.Helper.update(context, repo, values); + } + } + + /** + * FDroid works with three copies of the signing certificate: + *
  • in the downloaded jar
  • + *
  • in the index XML
  • + *
  • stored in the local database
  • + * It would work better removing the copy from the index XML, but it needs to stay + * there for backwards compatibility since the old TOFU process requires it. Therefore, + * since all three have to be present, all three are compared. + * + * @param certFromIndexXml the cert written into the header of the index XML + * @param rawCertFromJar the {@link X509Certificate} embedded in the downloaded jar + */ + private void verifyCerts(String certFromIndexXml, X509Certificate rawCertFromJar) throws UpdateException { + // convert binary data to string version that is used in FDroid's database + String certFromJar = Hasher.hex(rawCertFromJar); + + // repo and repo.pubkey must be pre-loaded from the database + if (repo == null + || TextUtils.isEmpty(repo.pubkey) + || TextUtils.isEmpty(certFromJar) + || TextUtils.isEmpty(certFromIndexXml)) + throw new UpdateException(repo, "A empty repo or signing certificate is invalid!"); + + // though its called repo.pubkey, its actually a X509 certificate + if (repo.pubkey.equals(certFromJar) + && repo.pubkey.equals(certFromIndexXml) + && certFromIndexXml.equals(certFromJar)) { + return; // we have a match! + } + throw new UpdateException(repo, "Signing certificate does not match!"); } } diff --git a/F-Droid/src/org/fdroid/fdroid/RepoXMLHandler.java b/F-Droid/src/org/fdroid/fdroid/RepoXMLHandler.java index 8655f9edc..e1c2c7642 100644 --- a/F-Droid/src/org/fdroid/fdroid/RepoXMLHandler.java +++ b/F-Droid/src/org/fdroid/fdroid/RepoXMLHandler.java @@ -32,6 +32,9 @@ import org.xml.sax.helpers.DefaultHandler; import java.util.ArrayList; import java.util.List; +/** + * Parses the index.xml into Java data structures. + */ public class RepoXMLHandler extends DefaultHandler { // The repo we're processing. @@ -49,27 +52,18 @@ public class RepoXMLHandler extends DefaultHandler { private int version = -1; private int maxage = -1; - // After processing the XML, this will be null if the index specified a - // public key - otherwise a public key. This is used for TOFU where an - // index.xml is read on the first connection, and a signed index.jar is - // expected on all subsequent connections. - private String pubkey; + /** the X.509 signing certificate stored in the header of index.xml */ + private String signingCertFromIndexXml; private String name; private String description; private String hashType; - private int progressCounter = 0; - private final ProgressListener progressListener; - - private int totalAppCount; - - public RepoXMLHandler(Repo repo, ProgressListener listener) { + public RepoXMLHandler(Repo repo) { this.repo = repo; - pubkey = null; + signingCertFromIndexXml = null; name = null; description = null; - progressListener = listener; } public List getApps() { return apps; } @@ -84,7 +78,7 @@ public class RepoXMLHandler extends DefaultHandler { public String getName() { return name; } - public String getPubKey() { return pubkey; } + public String getSigningCertFromIndexXml() { return signingCertFromIndexXml; } @Override public void characters(char[] ch, int start, int length) { @@ -248,10 +242,7 @@ public class RepoXMLHandler extends DefaultHandler { super.startElement(uri, localName, qName, attributes); if (localName.equals("repo")) { - final String pk = attributes.getValue("", "pubkey"); - if (pk != null) - pubkey = pk; - + signingCertFromIndexXml = attributes.getValue("", "pubkey"); maxage = Utils.parseInt(attributes.getValue("", "maxage"), -1); version = Utils.parseInt(attributes.getValue("", "version"), -1); @@ -265,16 +256,6 @@ public class RepoXMLHandler extends DefaultHandler { } else if (localName.equals("application") && curapp == null) { curapp = new App(); curapp.id = attributes.getValue("", "id"); - /* show progress for the first 25, then start skipping every 25 */ - if (totalAppCount < 25 || progressCounter % (totalAppCount / 25) == 0) { - Bundle data = new Bundle(1); - data.putString(RepoUpdater.PROGRESS_DATA_REPO_ADDRESS, repo.address); - progressListener.onProgress( - new ProgressListener.Event( - RepoUpdater.PROGRESS_TYPE_PROCESS_XML, - progressCounter, totalAppCount, data)); - } - progressCounter++; } else if (localName.equals("package") && curapp != null && curapk == null) { curapk = new Apk(); curapk.id = curapp.id; @@ -287,10 +268,6 @@ public class RepoXMLHandler extends DefaultHandler { curchars.setLength(0); } - public void setTotalAppCount(int totalAppCount) { - this.totalAppCount = totalAppCount; - } - private String cleanWhiteSpace(String str) { return str.replaceAll("\n", " ").replaceAll(" ", " "); } diff --git a/F-Droid/src/org/fdroid/fdroid/UpdateService.java b/F-Droid/src/org/fdroid/fdroid/UpdateService.java index 43f3157bf..ec63a7d49 100644 --- a/F-Droid/src/org/fdroid/fdroid/UpdateService.java +++ b/F-Droid/src/org/fdroid/fdroid/UpdateService.java @@ -769,7 +769,6 @@ public class UpdateService extends IntentService implements ProgressListener { Log.d(TAG, "Removing " + numDeleted + " apks that don't have any apks"); } - /** * Received progress event from the RepoXMLHandler. It could be progress * downloading from the repo, or perhaps processing the info from the repo. @@ -780,16 +779,16 @@ public class UpdateService extends IntentService implements ProgressListener { // TODO: Switch to passing through Bundles of data with the event, rather than a repo address. They are // now much more general purpose then just repo downloading. String repoAddress = event.getData().getString(RepoUpdater.PROGRESS_DATA_REPO_ADDRESS); + String downloadedSize = Utils.getFriendlySize(event.progress); + String totalSize = Utils.getFriendlySize(event.total); + int percent = (int) ((double) event.progress / event.total * 100); switch (event.type) { - case Downloader.EVENT_PROGRESS: - String downloadedSize = Utils.getFriendlySize(event.progress); - String totalSize = Utils.getFriendlySize(event.total); - int percent = (int)((double)event.progress/event.total * 100); - message = getString(R.string.status_download, repoAddress, downloadedSize, totalSize, percent); - break; - case RepoUpdater.PROGRESS_TYPE_PROCESS_XML: - message = getString(R.string.status_processing_xml, repoAddress, event.progress, event.total); - break; + case Downloader.EVENT_PROGRESS: + message = getString(R.string.status_download, repoAddress, downloadedSize, totalSize, percent); + break; + case RepoUpdater.PROGRESS_TYPE_PROCESS_XML: + message = getString(R.string.status_processing_xml_percent, repoAddress, downloadedSize, totalSize, percent); + break; } sendStatus(STATUS_INFO, message); } diff --git a/F-Droid/src/org/fdroid/fdroid/Utils.java b/F-Droid/src/org/fdroid/fdroid/Utils.java index 42cd96ef3..cce9d61cd 100644 --- a/F-Droid/src/org/fdroid/fdroid/Utils.java +++ b/F-Droid/src/org/fdroid/fdroid/Utils.java @@ -45,7 +45,6 @@ import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -260,36 +259,6 @@ public final class Utils { return getMinMaxSdkVersion(context, packageName, "maxSdkVersion"); } - public static int countSubstringOccurrence(File file, String substring) throws IOException { - int count = 0; - FileReader input = null; - try { - int currentSubstringIndex = 0; - char[] buffer = new char[4096]; - - input = new FileReader(file); - int numRead = input.read(buffer); - while(numRead != -1) { - - for (char c : buffer) { - if (c == substring.charAt(currentSubstringIndex)) { - currentSubstringIndex++; - if (currentSubstringIndex == substring.length()) { - count++; - currentSubstringIndex = 0; - } - } else { - currentSubstringIndex = 0; - } - } - numRead = input.read(buffer); - } - } finally { - closeQuietly(input); - } - return count; - } - // return a fingerprint formatted for display public static String formatFingerprint(Context context, String fingerprint) { if (TextUtils.isEmpty(fingerprint) diff --git a/F-Droid/test/src/org/fdroid/fdroid/FileCompatTest.java b/F-Droid/test/src/org/fdroid/fdroid/FileCompatTest.java index 5ea33f3ee..22f1a2566 100644 --- a/F-Droid/test/src/org/fdroid/fdroid/FileCompatTest.java +++ b/F-Droid/test/src/org/fdroid/fdroid/FileCompatTest.java @@ -8,10 +8,11 @@ import org.fdroid.fdroid.compat.FileCompatForTest; import org.fdroid.fdroid.data.SanitizedFile; import java.io.File; +import java.util.UUID; public class FileCompatTest extends InstrumentationTestCase { - private static final String TAG = "org.fdroid.fdroid.FileCompatTest"; + private static final String TAG = "FileCompatTest"; private File dir; private SanitizedFile sourceFile; @@ -20,25 +21,22 @@ public class FileCompatTest extends InstrumentationTestCase { public void setUp() { dir = TestUtils.getWriteableDir(getInstrumentation()); sourceFile = SanitizedFile.knownSanitized(TestUtils.copyAssetToDir(getInstrumentation().getContext(), "simpleIndex.jar", dir)); - destFile = new SanitizedFile(dir, "dest.txt"); - assertTrue(!destFile.exists()); + destFile = new SanitizedFile(dir, "dest-" + UUID.randomUUID() + ".testproduct"); + assertFalse(destFile.exists()); assertTrue(sourceFile.getAbsolutePath() + " should exist.", sourceFile.exists()); } public void tearDown() { - if (sourceFile.exists()) { - assertTrue("Can't delete " + sourceFile.getAbsolutePath() + ".", sourceFile.delete()); + if (!sourceFile.delete()) { + System.out.println("Can't delete " + sourceFile.getAbsolutePath() + "."); } - if (destFile.exists()) { - assertTrue("Can't delete " + destFile.getAbsolutePath() + ".", destFile.delete()); + if (!destFile.delete()) { + System.out.println("Can't delete " + destFile.getAbsolutePath() + "."); } } public void testSymlinkRuntime() { - SanitizedFile destFile = new SanitizedFile(dir, "dest.txt"); - assertFalse(destFile.exists()); - FileCompatForTest.symlinkRuntimeTest(sourceFile, destFile); assertTrue(destFile.getAbsolutePath() + " should exist after symlinking", destFile.exists()); } @@ -46,9 +44,6 @@ public class FileCompatTest extends InstrumentationTestCase { public void testSymlinkLibcore() { if (Build.VERSION.SDK_INT >= 19) { - SanitizedFile destFile = new SanitizedFile(dir, "dest.txt"); - assertFalse(destFile.exists()); - FileCompatForTest.symlinkLibcoreTest(sourceFile, destFile); assertTrue(destFile.getAbsolutePath() + " should exist after symlinking", destFile.exists()); } else { @@ -59,9 +54,6 @@ public class FileCompatTest extends InstrumentationTestCase { public void testSymlinkOs() { if (Build.VERSION.SDK_INT >= 21 ) { - SanitizedFile destFile = new SanitizedFile(dir, "dest.txt"); - assertFalse(destFile.exists()); - FileCompatForTest.symlinkOsTest(sourceFile, destFile); assertTrue(destFile.getAbsolutePath() + " should exist after symlinking", destFile.exists()); } else { diff --git a/F-Droid/test/src/org/fdroid/fdroid/RepoUpdaterTest.java b/F-Droid/test/src/org/fdroid/fdroid/RepoUpdaterTest.java index 96b2ae6f5..1c5a95999 100644 --- a/F-Droid/test/src/org/fdroid/fdroid/RepoUpdaterTest.java +++ b/F-Droid/test/src/org/fdroid/fdroid/RepoUpdaterTest.java @@ -5,12 +5,11 @@ import android.annotation.TargetApi; import android.content.Context; import android.test.InstrumentationTestCase; -import org.apache.commons.io.FileUtils; import org.fdroid.fdroid.RepoUpdater.UpdateException; import org.fdroid.fdroid.data.Repo; import java.io.File; -import java.io.IOException; +import java.util.UUID; @TargetApi(8) public class RepoUpdaterTest extends InstrumentationTestCase { @@ -34,17 +33,12 @@ public class RepoUpdaterTest extends InstrumentationTestCase { public void testExtractIndexFromJar() { if (!testFilesDir.canWrite()) return; - File simpleIndexXml = TestUtils.copyAssetToDir(context, "simpleIndex.xml", testFilesDir); File simpleIndexJar = TestUtils.copyAssetToDir(context, "simpleIndex.jar", testFilesDir); - File testFile = null; // these are supposed to succeed try { - testFile = repoUpdater.getIndexFromFile(simpleIndexJar); - assertTrue(testFile.length() == simpleIndexXml.length()); - assertEquals(FileUtils.readFileToString(testFile), - FileUtils.readFileToString(simpleIndexXml)); - } catch (IOException | UpdateException e) { + repoUpdater.processDownloadedFile(simpleIndexJar, UUID.randomUUID().toString()); + } catch (UpdateException e) { e.printStackTrace(); fail(); } @@ -55,7 +49,8 @@ public class RepoUpdaterTest extends InstrumentationTestCase { return; // this is supposed to fail try { - repoUpdater.getIndexFromFile(TestUtils.copyAssetToDir(context, "simpleIndexWithoutSignature.jar", testFilesDir)); + File jarFile = TestUtils.copyAssetToDir(context, "simpleIndexWithoutSignature.jar", testFilesDir); + repoUpdater.processDownloadedFile(jarFile, UUID.randomUUID().toString()); fail(); } catch (UpdateException e) { // success! @@ -67,7 +62,8 @@ public class RepoUpdaterTest extends InstrumentationTestCase { return; // this is supposed to fail try { - repoUpdater.getIndexFromFile(TestUtils.copyAssetToDir(context, "simpleIndexWithCorruptedManifest.jar", testFilesDir)); + File jarFile = TestUtils.copyAssetToDir(context, "simpleIndexWithCorruptedManifest.jar", testFilesDir); + repoUpdater.processDownloadedFile(jarFile, UUID.randomUUID().toString()); fail(); } catch (UpdateException e) { e.printStackTrace(); @@ -82,7 +78,8 @@ public class RepoUpdaterTest extends InstrumentationTestCase { return; // this is supposed to fail try { - repoUpdater.getIndexFromFile(TestUtils.copyAssetToDir(context, "simpleIndexWithCorruptedSignature.jar", testFilesDir)); + File jarFile = TestUtils.copyAssetToDir(context, "simpleIndexWithCorruptedSignature.jar", testFilesDir); + repoUpdater.processDownloadedFile(jarFile, UUID.randomUUID().toString()); fail(); } catch (UpdateException e) { e.printStackTrace(); @@ -97,7 +94,8 @@ public class RepoUpdaterTest extends InstrumentationTestCase { return; // this is supposed to fail try { - repoUpdater.getIndexFromFile(TestUtils.copyAssetToDir(context, "simpleIndexWithCorruptedCertificate.jar", testFilesDir)); + File jarFile = TestUtils.copyAssetToDir(context, "simpleIndexWithCorruptedCertificate.jar", testFilesDir); + repoUpdater.processDownloadedFile(jarFile, UUID.randomUUID().toString()); fail(); } catch (UpdateException e) { e.printStackTrace(); @@ -112,7 +110,8 @@ public class RepoUpdaterTest extends InstrumentationTestCase { return; // this is supposed to fail try { - repoUpdater.getIndexFromFile(TestUtils.copyAssetToDir(context, "simpleIndexWithCorruptedEverything.jar", testFilesDir)); + File jarFile = TestUtils.copyAssetToDir(context, "simpleIndexWithCorruptedEverything.jar", testFilesDir); + repoUpdater.processDownloadedFile(jarFile, UUID.randomUUID().toString()); fail(); } catch (UpdateException e) { e.printStackTrace(); @@ -127,7 +126,8 @@ public class RepoUpdaterTest extends InstrumentationTestCase { return; // this is supposed to fail try { - repoUpdater.getIndexFromFile(TestUtils.copyAssetToDir(context, "masterKeyIndex.jar", testFilesDir)); + File jarFile = TestUtils.copyAssetToDir(context, "masterKeyIndex.jar", testFilesDir); + repoUpdater.processDownloadedFile(jarFile, UUID.randomUUID().toString()); fail(); } catch (UpdateException | SecurityException e) { // success!