Search in sources :

Example 1 with RepoPersister

use of org.fdroid.fdroid.data.RepoPersister in project fdroidclient by f-droid.

the class IndexV1Updater method processIndexV1.

/**
 * Parses the index and feeds it to the database via {@link Repo}, {@link App},
 * and {@link Apk} instances.  This uses {@link RepoPersister}  to add the apps
 * and packages to the database in {@link RepoPersister#saveToDb(App, List)}
 * to write the {@link Repo}, and commit the whole thing in
 * {@link RepoPersister#commit(ContentValues, long)}.  One confusing thing about this
 * whole process is that {@link RepoPersister} needs to first create and entry
 * in the database, then fetch the ID from the database to populate
 * {@link Repo#id}.  That has to happen first, then the rest of the {@code Repo}
 * data must be added later.
 *
 * @param indexInputStream {@link InputStream} to {@code index-v1.json}
 * @param etag             the {@code etag} value from HTTP headers
 * @throws IOException
 * @throws UpdateException
 */
public void processIndexV1(InputStream indexInputStream, JarEntry indexEntry, String etag) throws IOException, UpdateException {
    Utils.Profiler profiler = new Utils.Profiler(TAG);
    profiler.log("Starting to process index-v1.json");
    ObjectMapper mapper = getObjectMapperInstance(repo.getId());
    JsonFactory f = mapper.getFactory();
    JsonParser parser = f.createParser(indexInputStream);
    HashMap<String, Object> repoMap = null;
    App[] apps = null;
    Map<String, String[]> requests = null;
    Map<String, List<Apk>> packages = null;
    // go into the main object block
    parser.nextToken();
    while (true) {
        String fieldName = parser.nextFieldName();
        if (fieldName == null) {
            break;
        }
        switch(fieldName) {
            case "repo":
                repoMap = parseRepo(mapper, parser);
                break;
            case "requests":
                requests = parseRequests(mapper, parser);
                break;
            case "apps":
                apps = parseApps(mapper, parser);
                break;
            case "packages":
                packages = parsePackages(mapper, parser);
                break;
        }
    }
    // ensure resources get cleaned up timely and properly
    parser.close();
    profiler.log("Finished processing index-v1.json. Now verifying certificate...");
    if (repoMap == null) {
        return;
    }
    long timestamp = (Long) repoMap.get("timestamp") / 1000;
    if (repo.timestamp > timestamp) {
        throw new IndexUpdater.UpdateException(repo, "index.jar is older that current index! " + timestamp + " < " + repo.timestamp);
    }
    X509Certificate certificate = getSigningCertFromJar(indexEntry);
    verifySigningCertificate(certificate);
    profiler.log("Certificate verified. Now saving to database...");
    // timestamp is absolutely required
    repo.timestamp = timestamp;
    // below are optional, can be null
    repo.lastetag = etag;
    repo.name = getStringRepoValue(repoMap, "name");
    repo.icon = getStringRepoValue(repoMap, "icon");
    repo.description = getStringRepoValue(repoMap, "description");
    // ensure the canonical URL is included in the "mirrors" list as the first entry
    LinkedHashSet<String> mirrors = new LinkedHashSet<>();
    mirrors.add(repo.address);
    mirrors.addAll(getStringListRepoValue(repoMap, "mirrors"));
    repo.mirrors = mirrors.toArray(new String[mirrors.size()]);
    // below are optional, can be default value
    repo.maxage = getIntRepoValue(repoMap, "maxage");
    repo.version = getIntRepoValue(repoMap, "version");
    if (TextUtils.isEmpty(platformSigCache)) {
        PackageInfo androidPackageInfo = Utils.getPackageInfoWithSignatures(context, "android");
        platformSigCache = Utils.getPackageSig(androidPackageInfo);
    }
    RepoPersister repoPersister = new RepoPersister(context, repo);
    if (apps != null && apps.length > 0) {
        int appCount = 0;
        for (App app : apps) {
            appCount++;
            List<Apk> apks = null;
            if (packages != null) {
                apks = packages.get(app.packageName);
            }
            if (apks == null) {
                Log.i(TAG, "processIndexV1 empty packages");
                apks = new ArrayList<>(0);
            }
            if (apks.size() > 0) {
                app.preferredSigner = apks.get(0).sig;
                app.isApk = true;
                for (Apk apk : apks) {
                    if (!apk.isApk()) {
                        app.isApk = false;
                    } else if (apk.sig.equals(platformSigCache)) {
                        app.preferredSigner = platformSigCache;
                    }
                }
            }
            if (appCount % 50 == 0) {
                notifyProcessingApps(appCount, apps.length);
            }
            repoPersister.saveToDb(app, apks);
        }
    }
    profiler.log("Saved to database, but only a temporary table. Now persisting to database...");
    notifyCommittingToDb();
    ContentValues contentValues = new ContentValues();
    contentValues.put(Schema.RepoTable.Cols.LAST_UPDATED, Utils.formatTime(new Date(), ""));
    contentValues.put(Schema.RepoTable.Cols.TIMESTAMP, repo.timestamp);
    contentValues.put(Schema.RepoTable.Cols.LAST_ETAG, repo.lastetag);
    if (repo.version != Repo.INT_UNSET_VALUE) {
        contentValues.put(Schema.RepoTable.Cols.VERSION, repo.version);
    }
    if (repo.maxage != Repo.INT_UNSET_VALUE) {
        contentValues.put(Schema.RepoTable.Cols.MAX_AGE, repo.maxage);
    }
    if (repo.description != null) {
        contentValues.put(Schema.RepoTable.Cols.DESCRIPTION, repo.description);
    }
    if (repo.name != null) {
        contentValues.put(Schema.RepoTable.Cols.NAME, repo.name);
    }
    if (repo.icon != null) {
        contentValues.put(Schema.RepoTable.Cols.ICON, repo.icon);
    }
    if (repo.mirrors != null && repo.mirrors.length > 0) {
        contentValues.put(Schema.RepoTable.Cols.MIRRORS, Utils.serializeCommaSeparatedString(repo.mirrors));
    }
    repoPersister.commit(contentValues, repo.getId());
    profiler.log("Persisted to database.");
    if (repo.pushRequests == Repo.PUSH_REQUEST_ACCEPT_ALWAYS) {
        processRepoPushRequests(requests);
        Utils.debugLog(TAG, "Completed Repo Push Requests: " + requests);
    }
}
Also used : App(org.fdroid.fdroid.data.App) LinkedHashSet(java.util.LinkedHashSet) JsonFactory(com.fasterxml.jackson.core.JsonFactory) ArrayList(java.util.ArrayList) List(java.util.List) ObjectMapper(com.fasterxml.jackson.databind.ObjectMapper) JsonParser(com.fasterxml.jackson.core.JsonParser) RepoPersister(org.fdroid.fdroid.data.RepoPersister) ContentValues(android.content.ContentValues) PackageInfo(android.content.pm.PackageInfo) X509Certificate(java.security.cert.X509Certificate) Date(java.util.Date) TextUtils(android.text.TextUtils) FileUtils(org.apache.commons.io.FileUtils) Apk(org.fdroid.fdroid.data.Apk)

Aggregations

ContentValues (android.content.ContentValues)1 PackageInfo (android.content.pm.PackageInfo)1 TextUtils (android.text.TextUtils)1 JsonFactory (com.fasterxml.jackson.core.JsonFactory)1 JsonParser (com.fasterxml.jackson.core.JsonParser)1 ObjectMapper (com.fasterxml.jackson.databind.ObjectMapper)1 X509Certificate (java.security.cert.X509Certificate)1 ArrayList (java.util.ArrayList)1 Date (java.util.Date)1 LinkedHashSet (java.util.LinkedHashSet)1 List (java.util.List)1 FileUtils (org.apache.commons.io.FileUtils)1 Apk (org.fdroid.fdroid.data.Apk)1 App (org.fdroid.fdroid.data.App)1 RepoPersister (org.fdroid.fdroid.data.RepoPersister)1