basic support for repo push requests, configed in default_repos

This allows whitelabel versions of apps to specify built-in app repos that
have push requests accepted by default.  This is useful for the case where
there is a central manager of the core apps that are installed.

https://gitlab.com/fdroid/fdroidserver/issues/177
This commit is contained in:
Hans-Christoph Steiner 2016-09-01 22:14:36 +02:00
parent d34a1285e8
commit 5c9dd1a11e
11 changed files with 403 additions and 23 deletions

View File

@ -1,18 +1,48 @@
/*
* Copyright (C) 2016 Blue Jay Wireless
* Copyright (C) 2015-2016 Daniel Martí <mvdan@mvdan.cc>
* Copyright (C) 2014-2016 Hans-Christoph Steiner <hans@eds.org>
* Copyright (C) 2014-2016 Peter Serwylo <peter@serwylo.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.fdroid.fdroid; package org.fdroid.fdroid;
import android.content.ContentResolver;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.data.ApkProvider;
import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.App;
import org.fdroid.fdroid.data.AppProvider;
import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.data.RepoPersister; import org.fdroid.fdroid.data.RepoPersister;
import org.fdroid.fdroid.data.RepoProvider; import org.fdroid.fdroid.data.RepoProvider;
import org.fdroid.fdroid.data.RepoPushRequest;
import org.fdroid.fdroid.data.Schema.RepoTable; import org.fdroid.fdroid.data.Schema.RepoTable;
import org.fdroid.fdroid.installer.InstallManagerService;
import org.fdroid.fdroid.installer.InstallerService;
import org.fdroid.fdroid.net.Downloader; import org.fdroid.fdroid.net.Downloader;
import org.fdroid.fdroid.net.DownloaderFactory; import org.fdroid.fdroid.net.DownloaderFactory;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
@ -27,6 +57,7 @@ import java.net.URL;
import java.security.CodeSigner; import java.security.CodeSigner;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
@ -37,12 +68,15 @@ import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.SAXParserFactory;
/** /**
* Responsible for updating an individual repository. This will: * Updates the local database with a repository's app/apk metadata and verifying
* * Download the index.jar * the JAR signature on the file received from the repository. As an overview:
* * Verify that it is signed correctly and by the correct certificate * <ul>
* * Parse the index.xml from the .jar file * <li>Download the {@code index.jar}
* * Save the resulting repo, apps, and apks to the database. * <li>Verify that it is signed correctly and by the correct certificate
* * <li>Parse the {@code index.xml} that is in {@code index.jar}
* <li>Save the resulting repo, apps, and apks to the database.
* <li>Process any push install/uninstall requests included in the repository
* </ul>
* <b>WARNING</b>: this class is the central piece of the entire security model of * <b>WARNING</b>: this class is the central piece of the entire security model of
* FDroid! Avoid modifying it when possible, if you absolutely must, be very, * FDroid! Avoid modifying it when possible, if you absolutely must, be very,
* very careful with the changes that you are making! * very careful with the changes that you are making!
@ -66,7 +100,10 @@ public class RepoUpdater {
private String cacheTag; private String cacheTag;
private X509Certificate signingCertFromJar; private X509Certificate signingCertFromJar;
@NonNull private final RepoPersister persister; @NonNull
private final RepoPersister persister;
private final List<RepoPushRequest> repoPushRequestList = new ArrayList<>();
/** /**
* Updates an app repo as read out of the database into a {@link Repo} instance. * Updates an app repo as read out of the database into a {@link Repo} instance.
@ -148,6 +185,7 @@ public class RepoUpdater {
// successful download, then we will have a file ready to use: // successful download, then we will have a file ready to use:
cacheTag = downloader.getCacheTag(); cacheTag = downloader.getCacheTag();
processDownloadedFile(downloader.outputFile); processDownloadedFile(downloader.outputFile);
processRepoPushRequests();
} }
} }
@ -170,6 +208,11 @@ public class RepoUpdater {
throw new RuntimeException("Error while saving repo details to database.", e); throw new RuntimeException("Error while saving repo details to database.", e);
} }
} }
@Override
public void receiveRepoPushRequest(RepoPushRequest repoPushRequest) {
repoPushRequestList.add(repoPushRequest);
}
}; };
} }
@ -188,7 +231,7 @@ public class RepoUpdater {
JarFile jarFile = new JarFile(downloadedFile, true); JarFile jarFile = new JarFile(downloadedFile, true);
JarEntry indexEntry = (JarEntry) jarFile.getEntry("index.xml"); JarEntry indexEntry = (JarEntry) jarFile.getEntry("index.xml");
indexInputStream = new ProgressBufferedInputStream(jarFile.getInputStream(indexEntry), indexInputStream = new ProgressBufferedInputStream(jarFile.getInputStream(indexEntry),
processXmlProgressListener, new URL(repo.address), (int) indexEntry.getSize()); processXmlProgressListener, new URL(repo.address), (int) indexEntry.getSize());
// Process the index... // Process the index...
SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParserFactory factory = SAXParserFactory.newInstance();
@ -396,4 +439,52 @@ public class RepoUpdater {
throw new SigningException(repo, "Signing certificate does not match!"); throw new SigningException(repo, "Signing certificate does not match!");
} }
/**
* Server index XML can include optional {@code install} and {@code uninstall}
* requests. This processes those requests, figuring out whether the client
* should always accept, prompt the user, or ignore those requests on a
* per repo basis.
*/
private void processRepoPushRequests() {
PackageManager pm = context.getPackageManager();
for (RepoPushRequest repoPushRequest : repoPushRequestList) {
String packageName = repoPushRequest.packageName;
PackageInfo packageInfo = null;
try {
packageInfo = pm.getPackageInfo(packageName, 0);
} catch (PackageManager.NameNotFoundException e) {
// ignored
}
if (RepoPushRequest.INSTALL.equals(repoPushRequest.request)) {
ContentResolver cr = context.getContentResolver();
App app = AppProvider.Helper.findByPackageName(cr, packageName);
int versionCode;
if (repoPushRequest.versionCode == null) {
versionCode = app.suggestedVersionCode;
} else {
versionCode = repoPushRequest.versionCode;
}
if (packageInfo != null && versionCode == packageInfo.versionCode) {
Utils.debugLog(TAG, repoPushRequest + " already installed, ignoring");
} else {
Apk apk = ApkProvider.Helper.find(context, packageName, versionCode);
InstallManagerService.queue(context, app, apk);
}
} else if (RepoPushRequest.UNINSTALL.equals(repoPushRequest.request)) {
if (packageInfo == null) {
Utils.debugLog(TAG, "ignoring request, not installed: " + repoPushRequest);
continue;
}
if (repoPushRequest.versionCode == null
|| repoPushRequest.versionCode == packageInfo.versionCode) {
InstallerService.uninstall(context, packageName);
} else {
Utils.debugLog(TAG, "ignoring request based on versionCode:" + repoPushRequest);
}
} else {
Utils.debugLog(TAG, "Unknown Repo Push Request: " + repoPushRequest.request);
}
}
}
} }

View File

@ -25,6 +25,7 @@ import android.support.annotation.Nullable;
import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.App;
import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.data.RepoPushRequest;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.DefaultHandler;
@ -64,6 +65,8 @@ public class RepoXMLHandler extends DefaultHandler {
void receiveRepo(String name, String description, String signingCert, int maxage, int version, long timestamp); void receiveRepo(String name, String description, String signingCert, int maxage, int version, long timestamp);
void receiveApp(App app, List<Apk> packages); void receiveApp(App app, List<Apk> packages);
void receiveRepoPushRequest(RepoPushRequest repoPushRequest);
} }
private final IndexReceiver receiver; private final IndexReceiver receiver;
@ -250,6 +253,10 @@ public class RepoXMLHandler extends DefaultHandler {
receiver.receiveRepo(repoName, repoDescription, repoSigningCert, repoMaxAge, repoVersion, repoTimestamp); receiver.receiveRepo(repoName, repoDescription, repoSigningCert, repoMaxAge, repoVersion, repoTimestamp);
} }
private void onRepoPushRequestParsed(RepoPushRequest repoPushRequest) {
receiver.receiveRepoPushRequest(repoPushRequest);
}
@Override @Override
public void startElement(String uri, String localName, String qName, public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException { Attributes attributes) throws SAXException {
@ -262,6 +269,15 @@ public class RepoXMLHandler extends DefaultHandler {
repoName = cleanWhiteSpace(attributes.getValue("", "name")); repoName = cleanWhiteSpace(attributes.getValue("", "name"));
repoDescription = cleanWhiteSpace(attributes.getValue("", "description")); repoDescription = cleanWhiteSpace(attributes.getValue("", "description"));
repoTimestamp = parseLong(attributes.getValue("", "timestamp"), 0); repoTimestamp = parseLong(attributes.getValue("", "timestamp"), 0);
} else if (RepoPushRequest.INSTALL.equals(localName)
|| RepoPushRequest.UNINSTALL.equals(localName)) {
if (repo.pushRequests == Repo.PUSH_REQUEST_ACCEPT_ALWAYS) {
RepoPushRequest r = new RepoPushRequest(
localName,
attributes.getValue("packageName"),
attributes.getValue("versionCode"));
onRepoPushRequestParsed(r);
}
} else if ("application".equals(localName) && curapp == null) { } else if ("application".equals(localName) && curapp == null) {
curapp = new App(); curapp = new App();
curapp.packageName = attributes.getValue("", "id"); curapp.packageName = attributes.getValue("", "id");

View File

@ -1,3 +1,26 @@
/*
* Copyright (C) 2016 Blue Jay Wireless
* Copyright (C) 2015-2016 Daniel Martí <mvdan@mvdan.cc>
* Copyright (C) 2015 Christian Morgner
* Copyright (C) 2014-2016 Hans-Christoph Steiner <hans@eds.org>
* Copyright (C) 2013-2016 Peter Serwylo <peter@serwylo.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.fdroid.fdroid.data; package org.fdroid.fdroid.data;
import android.content.ContentValues; import android.content.ContentValues;
@ -23,6 +46,8 @@ class DBHelper extends SQLiteOpenHelper {
private static final String TAG = "DBHelper"; private static final String TAG = "DBHelper";
public static final int REPO_XML_ARG_COUNT = 8;
private static final String DATABASE_NAME = "fdroid"; private static final String DATABASE_NAME = "fdroid";
private static final String CREATE_TABLE_REPO = "create table " private static final String CREATE_TABLE_REPO = "create table "
@ -42,7 +67,8 @@ class DBHelper extends SQLiteOpenHelper {
+ RepoTable.Cols.IS_SWAP + " integer boolean default 0," + RepoTable.Cols.IS_SWAP + " integer boolean default 0,"
+ RepoTable.Cols.USERNAME + " string, " + RepoTable.Cols.USERNAME + " string, "
+ RepoTable.Cols.PASSWORD + " string," + RepoTable.Cols.PASSWORD + " string,"
+ RepoTable.Cols.TIMESTAMP + " integer not null default 0" + RepoTable.Cols.TIMESTAMP + " integer not null default 0, "
+ RepoTable.Cols.PUSH_REQUESTS + " integer not null default " + Repo.PUSH_REQUEST_IGNORE
+ ");"; + ");";
static final String CREATE_TABLE_APK = static final String CREATE_TABLE_APK =
@ -120,7 +146,7 @@ class DBHelper extends SQLiteOpenHelper {
+ " );"; + " );";
private static final String DROP_TABLE_INSTALLED_APP = "DROP TABLE " + InstalledAppTable.NAME + ";"; private static final String DROP_TABLE_INSTALLED_APP = "DROP TABLE " + InstalledAppTable.NAME + ";";
private static final int DB_VERSION = 61; private static final int DB_VERSION = 62;
private final Context context; private final Context context;
@ -244,14 +270,15 @@ class DBHelper extends SQLiteOpenHelper {
defaultRepos[offset + 3], // version defaultRepos[offset + 3], // version
defaultRepos[offset + 4], // enabled defaultRepos[offset + 4], // enabled
defaultRepos[offset + 5], // priority defaultRepos[offset + 5], // priority
defaultRepos[offset + 6] // pubkey defaultRepos[offset + 6], // pushRequests
defaultRepos[offset + 7] // pubkey
); );
} }
} }
private void insertRepo(SQLiteDatabase db, String name, String address, private void insertRepo(SQLiteDatabase db, String name, String address,
String description, String version, String enabled, String description, String version, String enabled,
String priority, String pubKey) { String priority, String pushRequests, String pubKey) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(RepoTable.Cols.ADDRESS, address); values.put(RepoTable.Cols.ADDRESS, address);
values.put(RepoTable.Cols.NAME, name); values.put(RepoTable.Cols.NAME, name);
@ -265,7 +292,21 @@ class DBHelper extends SQLiteOpenHelper {
values.put(RepoTable.Cols.LAST_ETAG, (String) null); values.put(RepoTable.Cols.LAST_ETAG, (String) null);
values.put(RepoTable.Cols.TIMESTAMP, 0); values.put(RepoTable.Cols.TIMESTAMP, 0);
Utils.debugLog(TAG, "Adding repository " + name); switch (pushRequests) {
case "ignore":
values.put(RepoTable.Cols.PUSH_REQUESTS, Repo.PUSH_REQUEST_IGNORE);
break;
case "prompt":
values.put(RepoTable.Cols.PUSH_REQUESTS, Repo.PUSH_REQUEST_PROMPT);
break;
case "always":
values.put(RepoTable.Cols.PUSH_REQUESTS, Repo.PUSH_REQUEST_ACCEPT_ALWAYS);
break;
default:
throw new IllegalArgumentException(pushRequests + " is not a supported option!");
}
Utils.debugLog(TAG, "Adding repository " + name + " with push requests as " + pushRequests);
db.insert(RepoTable.NAME, null, values); db.insert(RepoTable.NAME, null, values);
} }
@ -302,6 +343,7 @@ class DBHelper extends SQLiteOpenHelper {
removeApkPackageNameColumn(db, oldVersion); removeApkPackageNameColumn(db, oldVersion);
addAppPrefsTable(db, oldVersion); addAppPrefsTable(db, oldVersion);
lowerCaseApkHashes(db, oldVersion); lowerCaseApkHashes(db, oldVersion);
supportRepoPushRequests(db, oldVersion);
} }
private void lowerCaseApkHashes(SQLiteDatabase db, int oldVersion) { private void lowerCaseApkHashes(SQLiteDatabase db, int oldVersion) {
@ -490,13 +532,13 @@ class DBHelper extends SQLiteOpenHelper {
} }
private void insertNameAndDescription(SQLiteDatabase db, private void insertNameAndDescription(SQLiteDatabase db,
int addressResId, int nameResId, int descriptionResId) { String name, String address, String description) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.clear(); values.clear();
values.put(RepoTable.Cols.NAME, context.getString(nameResId)); values.put(RepoTable.Cols.NAME, name);
values.put(RepoTable.Cols.DESCRIPTION, context.getString(descriptionResId)); values.put(RepoTable.Cols.DESCRIPTION, description);
db.update(RepoTable.NAME, values, RepoTable.Cols.ADDRESS + " = ?", new String[] { db.update(RepoTable.NAME, values, RepoTable.Cols.ADDRESS + " = ?", new String[]{
context.getString(addressResId), address,
}); });
} }
@ -770,6 +812,17 @@ class DBHelper extends SQLiteOpenHelper {
+ ApkTable.Cols.TARGET_SDK_VERSION + " integer"); + ApkTable.Cols.TARGET_SDK_VERSION + " integer");
} }
private void supportRepoPushRequests(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 61) {
return;
}
Utils.debugLog(TAG, "Adding " + RepoTable.Cols.PUSH_REQUESTS
+ " columns to " + RepoTable.NAME);
db.execSQL("alter table " + RepoTable.NAME + " add column "
+ RepoTable.Cols.PUSH_REQUESTS + " integer not null default "
+ Repo.PUSH_REQUEST_IGNORE);
}
private static boolean columnExists(SQLiteDatabase db, String table, String column) { private static boolean columnExists(SQLiteDatabase db, String table, String column) {
Cursor cursor = db.rawQuery("select * from " + table + " limit 0,1", null); Cursor cursor = db.rawQuery("select * from " + table + " limit 0,1", null);
boolean exists = cursor.getColumnIndex(column) != -1; boolean exists = cursor.getColumnIndex(column) != -1;

View File

@ -1,3 +1,26 @@
/*
* Copyright (C) 2016 Blue Jay Wireless
* Copyright (C) 2014-2016 Daniel Martí <mvdan@mvdan.cc>
* Copyright (C) 2014-2016 Hans-Christoph Steiner <hans@eds.org>
* Copyright (C) 2014-2016 Peter Serwylo <peter@serwylo.com>
* Copyright (C) 2015 Christian Morgner
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.fdroid.fdroid.data; package org.fdroid.fdroid.data;
import android.content.ContentValues; import android.content.ContentValues;
@ -15,6 +38,10 @@ public class Repo extends ValueObject {
public static final int VERSION_DENSITY_SPECIFIC_ICONS = 11; public static final int VERSION_DENSITY_SPECIFIC_ICONS = 11;
public static final int PUSH_REQUEST_IGNORE = 0;
public static final int PUSH_REQUEST_PROMPT = 1;
public static final int PUSH_REQUEST_ACCEPT_ALWAYS = 2;
protected long id; protected long id;
public String address; public String address;
@ -44,6 +71,9 @@ public class Repo extends ValueObject {
/** When the signed repo index was generated, used to protect against replay attacks */ /** When the signed repo index was generated, used to protect against replay attacks */
public long timestamp; public long timestamp;
/** How to treat push requests included in this repo's index XML */
public int pushRequests = PUSH_REQUEST_IGNORE;
public Repo() { public Repo() {
} }
@ -101,6 +131,9 @@ public class Repo extends ValueObject {
case Cols.TIMESTAMP: case Cols.TIMESTAMP:
timestamp = cursor.getLong(i); timestamp = cursor.getLong(i);
break; break;
case Cols.PUSH_REQUESTS:
pushRequests = cursor.getInt(i);
break;
} }
} }
} }
@ -223,5 +256,9 @@ public class Repo extends ValueObject {
if (values.containsKey(Cols.TIMESTAMP)) { if (values.containsKey(Cols.TIMESTAMP)) {
timestamp = toInt(values.getAsInteger(Cols.TIMESTAMP)); timestamp = toInt(values.getAsInteger(Cols.TIMESTAMP));
} }
if (values.containsKey(Cols.PUSH_REQUESTS)) {
pushRequests = toInt(values.getAsInteger(Cols.PUSH_REQUESTS));
}
} }
} }

View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2016 Blue Jay Wireless
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.fdroid.fdroid.data;
import android.support.annotation.Nullable;
/**
* Represents action requests embedded in the index XML received from a repo.
* When {@link #versionCode} is {@code null}, that means that the
* {@code versionCode} was not specified by the server, and F-Droid should
* install the best available version.
*/
public class RepoPushRequest {
public static final String TAG = "RepoPushRequest";
public static final String INSTALL = "install";
public static final String UNINSTALL = "uninstall";
public final String request;
public final String packageName;
@Nullable
public final Integer versionCode;
public RepoPushRequest(String request, String packageName, String versionCode) {
this.request = request;
this.packageName = packageName;
Integer i;
try {
i = Integer.parseInt(versionCode);
} catch (NumberFormatException e) {
i = null;
}
this.versionCode = i;
}
@Override
public String toString() {
return request + " " + packageName + " " + versionCode;
}
}

View File

@ -196,11 +196,12 @@ public interface Schema {
String USERNAME = "username"; String USERNAME = "username";
String PASSWORD = "password"; String PASSWORD = "password";
String TIMESTAMP = "timestamp"; String TIMESTAMP = "timestamp";
String PUSH_REQUESTS = "pushRequests";
String[] ALL = { String[] ALL = {
_ID, ADDRESS, NAME, DESCRIPTION, IN_USE, PRIORITY, SIGNING_CERT, _ID, ADDRESS, NAME, DESCRIPTION, IN_USE, PRIORITY, SIGNING_CERT,
FINGERPRINT, MAX_AGE, LAST_UPDATED, LAST_ETAG, VERSION, IS_SWAP, FINGERPRINT, MAX_AGE, LAST_UPDATED, LAST_ETAG, VERSION, IS_SWAP,
USERNAME, PASSWORD, TIMESTAMP, USERNAME, PASSWORD, TIMESTAMP, PUSH_REQUESTS,
}; };
} }
} }

View File

@ -18,6 +18,8 @@
<item>1</item> <item>1</item>
<!-- priority --> <!-- priority -->
<item>10</item> <item>10</item>
<!-- push requests -->
<item>ignore</item>
<!-- pubkey --> <!-- pubkey -->
<item> <item>
3082035e30820246a00302010202044c49cd00300d06092a864886f70d01010505003071310b300906035504061302554b3110300e06035504081307556e6b6e6f776e3111300f0603550407130857657468657262793110300e060355040a1307556e6b6e6f776e3110300e060355040b1307556e6b6e6f776e311930170603550403131043696172616e2047756c746e69656b73301e170d3130303732333137313032345a170d3337313230383137313032345a3071310b300906035504061302554b3110300e06035504081307556e6b6e6f776e3111300f0603550407130857657468657262793110300e060355040a1307556e6b6e6f776e3110300e060355040b1307556e6b6e6f776e311930170603550403131043696172616e2047756c746e69656b7330820122300d06092a864886f70d01010105000382010f003082010a028201010096d075e47c014e7822c89fd67f795d23203e2a8843f53ba4e6b1bf5f2fd0e225938267cfcae7fbf4fe596346afbaf4070fdb91f66fbcdf2348a3d92430502824f80517b156fab00809bdc8e631bfa9afd42d9045ab5fd6d28d9e140afc1300917b19b7c6c4df4a494cf1f7cb4a63c80d734265d735af9e4f09455f427aa65a53563f87b336ca2c19d244fcbba617ba0b19e56ed34afe0b253ab91e2fdb1271f1b9e3c3232027ed8862a112f0706e234cf236914b939bcf959821ecb2a6c18057e070de3428046d94b175e1d89bd795e535499a091f5bc65a79d539a8d43891ec504058acb28c08393b5718b57600a211e803f4a634e5c57f25b9b8c4422c6fd90203010001300d06092a864886f70d0101050500038201010008e4ef699e9807677ff56753da73efb2390d5ae2c17e4db691d5df7a7b60fc071ae509c5414be7d5da74df2811e83d3668c4a0b1abc84b9fa7d96b4cdf30bba68517ad2a93e233b042972ac0553a4801c9ebe07bf57ebe9a3b3d6d663965260e50f3b8f46db0531761e60340a2bddc3426098397fda54044a17e5244549f9869b460ca5e6e216b6f6a2db0580b480ca2afe6ec6b46eedacfa4aa45038809ece0c5978653d6c85f678e7f5a2156d1bedd8117751e64a4b0dcd140f3040b021821a8d93aed8d01ba36db6c82372211fed714d9a32607038cdfd565bd529ffc637212aaa2c224ef22b603eccefb5bf1e085c191d4b24fe742b17ab3f55d4e6f05ef 3082035e30820246a00302010202044c49cd00300d06092a864886f70d01010505003071310b300906035504061302554b3110300e06035504081307556e6b6e6f776e3111300f0603550407130857657468657262793110300e060355040a1307556e6b6e6f776e3110300e060355040b1307556e6b6e6f776e311930170603550403131043696172616e2047756c746e69656b73301e170d3130303732333137313032345a170d3337313230383137313032345a3071310b300906035504061302554b3110300e06035504081307556e6b6e6f776e3111300f0603550407130857657468657262793110300e060355040a1307556e6b6e6f776e3110300e060355040b1307556e6b6e6f776e311930170603550403131043696172616e2047756c746e69656b7330820122300d06092a864886f70d01010105000382010f003082010a028201010096d075e47c014e7822c89fd67f795d23203e2a8843f53ba4e6b1bf5f2fd0e225938267cfcae7fbf4fe596346afbaf4070fdb91f66fbcdf2348a3d92430502824f80517b156fab00809bdc8e631bfa9afd42d9045ab5fd6d28d9e140afc1300917b19b7c6c4df4a494cf1f7cb4a63c80d734265d735af9e4f09455f427aa65a53563f87b336ca2c19d244fcbba617ba0b19e56ed34afe0b253ab91e2fdb1271f1b9e3c3232027ed8862a112f0706e234cf236914b939bcf959821ecb2a6c18057e070de3428046d94b175e1d89bd795e535499a091f5bc65a79d539a8d43891ec504058acb28c08393b5718b57600a211e803f4a634e5c57f25b9b8c4422c6fd90203010001300d06092a864886f70d0101050500038201010008e4ef699e9807677ff56753da73efb2390d5ae2c17e4db691d5df7a7b60fc071ae509c5414be7d5da74df2811e83d3668c4a0b1abc84b9fa7d96b4cdf30bba68517ad2a93e233b042972ac0553a4801c9ebe07bf57ebe9a3b3d6d663965260e50f3b8f46db0531761e60340a2bddc3426098397fda54044a17e5244549f9869b460ca5e6e216b6f6a2db0580b480ca2afe6ec6b46eedacfa4aa45038809ece0c5978653d6c85f678e7f5a2156d1bedd8117751e64a4b0dcd140f3040b021821a8d93aed8d01ba36db6c82372211fed714d9a32607038cdfd565bd529ffc637212aaa2c224ef22b603eccefb5bf1e085c191d4b24fe742b17ab3f55d4e6f05ef
@ -37,6 +39,8 @@
<item>0</item> <item>0</item>
<!-- priority --> <!-- priority -->
<item>20</item> <item>20</item>
<!-- push requests -->
<item>ignore</item>
<!-- pubkey --> <!-- pubkey -->
<item> <item>
3082035e30820246a00302010202044c49cd00300d06092a864886f70d01010505003071310b300906035504061302554b3110300e06035504081307556e6b6e6f776e3111300f0603550407130857657468657262793110300e060355040a1307556e6b6e6f776e3110300e060355040b1307556e6b6e6f776e311930170603550403131043696172616e2047756c746e69656b73301e170d3130303732333137313032345a170d3337313230383137313032345a3071310b300906035504061302554b3110300e06035504081307556e6b6e6f776e3111300f0603550407130857657468657262793110300e060355040a1307556e6b6e6f776e3110300e060355040b1307556e6b6e6f776e311930170603550403131043696172616e2047756c746e69656b7330820122300d06092a864886f70d01010105000382010f003082010a028201010096d075e47c014e7822c89fd67f795d23203e2a8843f53ba4e6b1bf5f2fd0e225938267cfcae7fbf4fe596346afbaf4070fdb91f66fbcdf2348a3d92430502824f80517b156fab00809bdc8e631bfa9afd42d9045ab5fd6d28d9e140afc1300917b19b7c6c4df4a494cf1f7cb4a63c80d734265d735af9e4f09455f427aa65a53563f87b336ca2c19d244fcbba617ba0b19e56ed34afe0b253ab91e2fdb1271f1b9e3c3232027ed8862a112f0706e234cf236914b939bcf959821ecb2a6c18057e070de3428046d94b175e1d89bd795e535499a091f5bc65a79d539a8d43891ec504058acb28c08393b5718b57600a211e803f4a634e5c57f25b9b8c4422c6fd90203010001300d06092a864886f70d0101050500038201010008e4ef699e9807677ff56753da73efb2390d5ae2c17e4db691d5df7a7b60fc071ae509c5414be7d5da74df2811e83d3668c4a0b1abc84b9fa7d96b4cdf30bba68517ad2a93e233b042972ac0553a4801c9ebe07bf57ebe9a3b3d6d663965260e50f3b8f46db0531761e60340a2bddc3426098397fda54044a17e5244549f9869b460ca5e6e216b6f6a2db0580b480ca2afe6ec6b46eedacfa4aa45038809ece0c5978653d6c85f678e7f5a2156d1bedd8117751e64a4b0dcd140f3040b021821a8d93aed8d01ba36db6c82372211fed714d9a32607038cdfd565bd529ffc637212aaa2c224ef22b603eccefb5bf1e085c191d4b24fe742b17ab3f55d4e6f05ef 3082035e30820246a00302010202044c49cd00300d06092a864886f70d01010505003071310b300906035504061302554b3110300e06035504081307556e6b6e6f776e3111300f0603550407130857657468657262793110300e060355040a1307556e6b6e6f776e3110300e060355040b1307556e6b6e6f776e311930170603550403131043696172616e2047756c746e69656b73301e170d3130303732333137313032345a170d3337313230383137313032345a3071310b300906035504061302554b3110300e06035504081307556e6b6e6f776e3111300f0603550407130857657468657262793110300e060355040a1307556e6b6e6f776e3110300e060355040b1307556e6b6e6f776e311930170603550403131043696172616e2047756c746e69656b7330820122300d06092a864886f70d01010105000382010f003082010a028201010096d075e47c014e7822c89fd67f795d23203e2a8843f53ba4e6b1bf5f2fd0e225938267cfcae7fbf4fe596346afbaf4070fdb91f66fbcdf2348a3d92430502824f80517b156fab00809bdc8e631bfa9afd42d9045ab5fd6d28d9e140afc1300917b19b7c6c4df4a494cf1f7cb4a63c80d734265d735af9e4f09455f427aa65a53563f87b336ca2c19d244fcbba617ba0b19e56ed34afe0b253ab91e2fdb1271f1b9e3c3232027ed8862a112f0706e234cf236914b939bcf959821ecb2a6c18057e070de3428046d94b175e1d89bd795e535499a091f5bc65a79d539a8d43891ec504058acb28c08393b5718b57600a211e803f4a634e5c57f25b9b8c4422c6fd90203010001300d06092a864886f70d0101050500038201010008e4ef699e9807677ff56753da73efb2390d5ae2c17e4db691d5df7a7b60fc071ae509c5414be7d5da74df2811e83d3668c4a0b1abc84b9fa7d96b4cdf30bba68517ad2a93e233b042972ac0553a4801c9ebe07bf57ebe9a3b3d6d663965260e50f3b8f46db0531761e60340a2bddc3426098397fda54044a17e5244549f9869b460ca5e6e216b6f6a2db0580b480ca2afe6ec6b46eedacfa4aa45038809ece0c5978653d6c85f678e7f5a2156d1bedd8117751e64a4b0dcd140f3040b021821a8d93aed8d01ba36db6c82372211fed714d9a32607038cdfd565bd529ffc637212aaa2c224ef22b603eccefb5bf1e085c191d4b24fe742b17ab3f55d4e6f05ef
@ -57,6 +61,8 @@
<item>0</item> <item>0</item>
<!-- priority --> <!-- priority -->
<item>10</item> <item>10</item>
<!-- push requests -->
<item>ignore</item>
<!-- pubkey --> <!-- pubkey -->
<item> <item>
308205d8308203c0020900a397b4da7ecda034300d06092a864886f70d01010505003081ad310b30090603550406130255533111300f06035504080c084e657720596f726b3111300f06035504070c084e657720596f726b31143012060355040b0c0b4644726f6964205265706f31193017060355040a0c10477561726469616e2050726f6a656374311d301b06035504030c14677561726469616e70726f6a6563742e696e666f3128302606092a864886f70d0109011619726f6f7440677561726469616e70726f6a6563742e696e666f301e170d3134303632363139333931385a170d3431313131303139333931385a3081ad310b30090603550406130255533111300f06035504080c084e657720596f726b3111300f06035504070c084e657720596f726b31143012060355040b0c0b4644726f6964205265706f31193017060355040a0c10477561726469616e2050726f6a656374311d301b06035504030c14677561726469616e70726f6a6563742e696e666f3128302606092a864886f70d0109011619726f6f7440677561726469616e70726f6a6563742e696e666f30820222300d06092a864886f70d01010105000382020f003082020a0282020100b3cd79121b9b883843be3c4482e320809106b0a23755f1dd3c7f46f7d315d7bb2e943486d61fc7c811b9294dcc6b5baac4340f8db2b0d5e14749e7f35e1fc211fdbc1071b38b4753db201c314811bef885bd8921ad86facd6cc3b8f74d30a0b6e2e6e576f906e9581ef23d9c03e926e06d1f033f28bd1e21cfa6a0e3ff5c9d8246cf108d82b488b9fdd55d7de7ebb6a7f64b19e0d6b2ab1380a6f9d42361770d1956701a7f80e2de568acd0bb4527324b1e0973e89595d91c8cc102d9248525ae092e2c9b69f7414f724195b81427f28b1d3d09a51acfe354387915fd9521e8c890c125fc41a12bf34d2a1b304067ab7251e0e9ef41833ce109e76963b0b256395b16b886bca21b831f1408f836146019e7908829e716e72b81006610a2af08301de5d067c9e114a1e5759db8a6be6a3cc2806bcfe6fafd41b5bc9ddddb3dc33d6f605b1ca7d8a9e0ecdd6390d38906649e68a90a717bea80fa220170eea0c86fc78a7e10dac7b74b8e62045a3ecca54e035281fdc9fe5920a855fde3c0be522e3aef0c087524f13d973dff3768158b01a5800a060c06b451ec98d627dd052eda804d0556f60dbc490d94e6e9dea62ffcafb5beffbd9fc38fb2f0d7050004fe56b4dda0a27bc47554e1e0a7d764e17622e71f83a475db286bc7862deee1327e2028955d978272ea76bf0b88e70a18621aba59ff0c5993ef5f0e5d6b6b98e68b70203010001300d06092a864886f70d0101050500038202010079c79c8ef408a20d243d8bd8249fb9a48350dc19663b5e0fce67a8dbcb7de296c5ae7bbf72e98a2020fb78f2db29b54b0e24b181aa1c1d333cc0303685d6120b03216a913f96b96eb838f9bff125306ae3120af838c9fc07ebb5100125436bd24ec6d994d0bff5d065221871f8410daf536766757239bf594e61c5432c9817281b985263bada8381292e543a49814061ae11c92a316e7dc100327b59e3da90302c5ada68c6a50201bda1fcce800b53f381059665dbabeeb0b50eb22b2d7d2d9b0aa7488ca70e67ac6c518adb8e78454a466501e89d81a45bf1ebc350896f2c3ae4b6679ecfbf9d32960d4f5b493125c7876ef36158562371193f600bc511000a67bdb7c664d018f99d9e589868d103d7e0994f166b2ba18ff7e67d8c4da749e44dfae1d930ae5397083a51675c409049dfb626a96246c0015ca696e94ebb767a20147834bf78b07fece3f0872b057c1c519ff882501995237d8206b0b3832f78753ebd8dcbd1d3d9f5ba733538113af6b407d960ec4353c50eb38ab29888238da843cd404ed8f4952f59e4bbc0035fc77a54846a9d419179c46af1b4a3b7fc98e4d312aaa29b9b7d79e739703dc0fa41c7280d5587709277ffa11c3620f5fba985b82c238ba19b17ebd027af9424be0941719919f620dd3bb3c3f11638363708aa11f858e153cf3a69bce69978b90e4a273836100aa1e617ba455cd00426847f 308205d8308203c0020900a397b4da7ecda034300d06092a864886f70d01010505003081ad310b30090603550406130255533111300f06035504080c084e657720596f726b3111300f06035504070c084e657720596f726b31143012060355040b0c0b4644726f6964205265706f31193017060355040a0c10477561726469616e2050726f6a656374311d301b06035504030c14677561726469616e70726f6a6563742e696e666f3128302606092a864886f70d0109011619726f6f7440677561726469616e70726f6a6563742e696e666f301e170d3134303632363139333931385a170d3431313131303139333931385a3081ad310b30090603550406130255533111300f06035504080c084e657720596f726b3111300f06035504070c084e657720596f726b31143012060355040b0c0b4644726f6964205265706f31193017060355040a0c10477561726469616e2050726f6a656374311d301b06035504030c14677561726469616e70726f6a6563742e696e666f3128302606092a864886f70d0109011619726f6f7440677561726469616e70726f6a6563742e696e666f30820222300d06092a864886f70d01010105000382020f003082020a0282020100b3cd79121b9b883843be3c4482e320809106b0a23755f1dd3c7f46f7d315d7bb2e943486d61fc7c811b9294dcc6b5baac4340f8db2b0d5e14749e7f35e1fc211fdbc1071b38b4753db201c314811bef885bd8921ad86facd6cc3b8f74d30a0b6e2e6e576f906e9581ef23d9c03e926e06d1f033f28bd1e21cfa6a0e3ff5c9d8246cf108d82b488b9fdd55d7de7ebb6a7f64b19e0d6b2ab1380a6f9d42361770d1956701a7f80e2de568acd0bb4527324b1e0973e89595d91c8cc102d9248525ae092e2c9b69f7414f724195b81427f28b1d3d09a51acfe354387915fd9521e8c890c125fc41a12bf34d2a1b304067ab7251e0e9ef41833ce109e76963b0b256395b16b886bca21b831f1408f836146019e7908829e716e72b81006610a2af08301de5d067c9e114a1e5759db8a6be6a3cc2806bcfe6fafd41b5bc9ddddb3dc33d6f605b1ca7d8a9e0ecdd6390d38906649e68a90a717bea80fa220170eea0c86fc78a7e10dac7b74b8e62045a3ecca54e035281fdc9fe5920a855fde3c0be522e3aef0c087524f13d973dff3768158b01a5800a060c06b451ec98d627dd052eda804d0556f60dbc490d94e6e9dea62ffcafb5beffbd9fc38fb2f0d7050004fe56b4dda0a27bc47554e1e0a7d764e17622e71f83a475db286bc7862deee1327e2028955d978272ea76bf0b88e70a18621aba59ff0c5993ef5f0e5d6b6b98e68b70203010001300d06092a864886f70d0101050500038202010079c79c8ef408a20d243d8bd8249fb9a48350dc19663b5e0fce67a8dbcb7de296c5ae7bbf72e98a2020fb78f2db29b54b0e24b181aa1c1d333cc0303685d6120b03216a913f96b96eb838f9bff125306ae3120af838c9fc07ebb5100125436bd24ec6d994d0bff5d065221871f8410daf536766757239bf594e61c5432c9817281b985263bada8381292e543a49814061ae11c92a316e7dc100327b59e3da90302c5ada68c6a50201bda1fcce800b53f381059665dbabeeb0b50eb22b2d7d2d9b0aa7488ca70e67ac6c518adb8e78454a466501e89d81a45bf1ebc350896f2c3ae4b6679ecfbf9d32960d4f5b493125c7876ef36158562371193f600bc511000a67bdb7c664d018f99d9e589868d103d7e0994f166b2ba18ff7e67d8c4da749e44dfae1d930ae5397083a51675c409049dfb626a96246c0015ca696e94ebb767a20147834bf78b07fece3f0872b057c1c519ff882501995237d8206b0b3832f78753ebd8dcbd1d3d9f5ba733538113af6b407d960ec4353c50eb38ab29888238da843cd404ed8f4952f59e4bbc0035fc77a54846a9d419179c46af1b4a3b7fc98e4d312aaa29b9b7d79e739703dc0fa41c7280d5587709277ffa11c3620f5fba985b82c238ba19b17ebd027af9424be0941719919f620dd3bb3c3f11638363708aa11f858e153cf3a69bce69978b90e4a273836100aa1e617ba455cd00426847f
@ -76,6 +82,8 @@
<item>0</item> <item>0</item>
<!-- priority --> <!-- priority -->
<item>20</item> <item>20</item>
<!-- push requests -->
<item>ignore</item>
<!-- pubkey --> <!-- pubkey -->
<item> <item>
308205d8308203c0020900a397b4da7ecda034300d06092a864886f70d01010505003081ad310b30090603550406130255533111300f06035504080c084e657720596f726b3111300f06035504070c084e657720596f726b31143012060355040b0c0b4644726f6964205265706f31193017060355040a0c10477561726469616e2050726f6a656374311d301b06035504030c14677561726469616e70726f6a6563742e696e666f3128302606092a864886f70d0109011619726f6f7440677561726469616e70726f6a6563742e696e666f301e170d3134303632363139333931385a170d3431313131303139333931385a3081ad310b30090603550406130255533111300f06035504080c084e657720596f726b3111300f06035504070c084e657720596f726b31143012060355040b0c0b4644726f6964205265706f31193017060355040a0c10477561726469616e2050726f6a656374311d301b06035504030c14677561726469616e70726f6a6563742e696e666f3128302606092a864886f70d0109011619726f6f7440677561726469616e70726f6a6563742e696e666f30820222300d06092a864886f70d01010105000382020f003082020a0282020100b3cd79121b9b883843be3c4482e320809106b0a23755f1dd3c7f46f7d315d7bb2e943486d61fc7c811b9294dcc6b5baac4340f8db2b0d5e14749e7f35e1fc211fdbc1071b38b4753db201c314811bef885bd8921ad86facd6cc3b8f74d30a0b6e2e6e576f906e9581ef23d9c03e926e06d1f033f28bd1e21cfa6a0e3ff5c9d8246cf108d82b488b9fdd55d7de7ebb6a7f64b19e0d6b2ab1380a6f9d42361770d1956701a7f80e2de568acd0bb4527324b1e0973e89595d91c8cc102d9248525ae092e2c9b69f7414f724195b81427f28b1d3d09a51acfe354387915fd9521e8c890c125fc41a12bf34d2a1b304067ab7251e0e9ef41833ce109e76963b0b256395b16b886bca21b831f1408f836146019e7908829e716e72b81006610a2af08301de5d067c9e114a1e5759db8a6be6a3cc2806bcfe6fafd41b5bc9ddddb3dc33d6f605b1ca7d8a9e0ecdd6390d38906649e68a90a717bea80fa220170eea0c86fc78a7e10dac7b74b8e62045a3ecca54e035281fdc9fe5920a855fde3c0be522e3aef0c087524f13d973dff3768158b01a5800a060c06b451ec98d627dd052eda804d0556f60dbc490d94e6e9dea62ffcafb5beffbd9fc38fb2f0d7050004fe56b4dda0a27bc47554e1e0a7d764e17622e71f83a475db286bc7862deee1327e2028955d978272ea76bf0b88e70a18621aba59ff0c5993ef5f0e5d6b6b98e68b70203010001300d06092a864886f70d0101050500038202010079c79c8ef408a20d243d8bd8249fb9a48350dc19663b5e0fce67a8dbcb7de296c5ae7bbf72e98a2020fb78f2db29b54b0e24b181aa1c1d333cc0303685d6120b03216a913f96b96eb838f9bff125306ae3120af838c9fc07ebb5100125436bd24ec6d994d0bff5d065221871f8410daf536766757239bf594e61c5432c9817281b985263bada8381292e543a49814061ae11c92a316e7dc100327b59e3da90302c5ada68c6a50201bda1fcce800b53f381059665dbabeeb0b50eb22b2d7d2d9b0aa7488ca70e67ac6c518adb8e78454a466501e89d81a45bf1ebc350896f2c3ae4b6679ecfbf9d32960d4f5b493125c7876ef36158562371193f600bc511000a67bdb7c664d018f99d9e589868d103d7e0994f166b2ba18ff7e67d8c4da749e44dfae1d930ae5397083a51675c409049dfb626a96246c0015ca696e94ebb767a20147834bf78b07fece3f0872b057c1c519ff882501995237d8206b0b3832f78753ebd8dcbd1d3d9f5ba733538113af6b407d960ec4353c50eb38ab29888238da843cd404ed8f4952f59e4bbc0035fc77a54846a9d419179c46af1b4a3b7fc98e4d312aaa29b9b7d79e739703dc0fa41c7280d5587709277ffa11c3620f5fba985b82c238ba19b17ebd027af9424be0941719919f620dd3bb3c3f11638363708aa11f858e153cf3a69bce69978b90e4a273836100aa1e617ba455cd00426847f 308205d8308203c0020900a397b4da7ecda034300d06092a864886f70d01010505003081ad310b30090603550406130255533111300f06035504080c084e657720596f726b3111300f06035504070c084e657720596f726b31143012060355040b0c0b4644726f6964205265706f31193017060355040a0c10477561726469616e2050726f6a656374311d301b06035504030c14677561726469616e70726f6a6563742e696e666f3128302606092a864886f70d0109011619726f6f7440677561726469616e70726f6a6563742e696e666f301e170d3134303632363139333931385a170d3431313131303139333931385a3081ad310b30090603550406130255533111300f06035504080c084e657720596f726b3111300f06035504070c084e657720596f726b31143012060355040b0c0b4644726f6964205265706f31193017060355040a0c10477561726469616e2050726f6a656374311d301b06035504030c14677561726469616e70726f6a6563742e696e666f3128302606092a864886f70d0109011619726f6f7440677561726469616e70726f6a6563742e696e666f30820222300d06092a864886f70d01010105000382020f003082020a0282020100b3cd79121b9b883843be3c4482e320809106b0a23755f1dd3c7f46f7d315d7bb2e943486d61fc7c811b9294dcc6b5baac4340f8db2b0d5e14749e7f35e1fc211fdbc1071b38b4753db201c314811bef885bd8921ad86facd6cc3b8f74d30a0b6e2e6e576f906e9581ef23d9c03e926e06d1f033f28bd1e21cfa6a0e3ff5c9d8246cf108d82b488b9fdd55d7de7ebb6a7f64b19e0d6b2ab1380a6f9d42361770d1956701a7f80e2de568acd0bb4527324b1e0973e89595d91c8cc102d9248525ae092e2c9b69f7414f724195b81427f28b1d3d09a51acfe354387915fd9521e8c890c125fc41a12bf34d2a1b304067ab7251e0e9ef41833ce109e76963b0b256395b16b886bca21b831f1408f836146019e7908829e716e72b81006610a2af08301de5d067c9e114a1e5759db8a6be6a3cc2806bcfe6fafd41b5bc9ddddb3dc33d6f605b1ca7d8a9e0ecdd6390d38906649e68a90a717bea80fa220170eea0c86fc78a7e10dac7b74b8e62045a3ecca54e035281fdc9fe5920a855fde3c0be522e3aef0c087524f13d973dff3768158b01a5800a060c06b451ec98d627dd052eda804d0556f60dbc490d94e6e9dea62ffcafb5beffbd9fc38fb2f0d7050004fe56b4dda0a27bc47554e1e0a7d764e17622e71f83a475db286bc7862deee1327e2028955d978272ea76bf0b88e70a18621aba59ff0c5993ef5f0e5d6b6b98e68b70203010001300d06092a864886f70d0101050500038202010079c79c8ef408a20d243d8bd8249fb9a48350dc19663b5e0fce67a8dbcb7de296c5ae7bbf72e98a2020fb78f2db29b54b0e24b181aa1c1d333cc0303685d6120b03216a913f96b96eb838f9bff125306ae3120af838c9fc07ebb5100125436bd24ec6d994d0bff5d065221871f8410daf536766757239bf594e61c5432c9817281b985263bada8381292e543a49814061ae11c92a316e7dc100327b59e3da90302c5ada68c6a50201bda1fcce800b53f381059665dbabeeb0b50eb22b2d7d2d9b0aa7488ca70e67ac6c518adb8e78454a466501e89d81a45bf1ebc350896f2c3ae4b6679ecfbf9d32960d4f5b493125c7876ef36158562371193f600bc511000a67bdb7c664d018f99d9e589868d103d7e0994f166b2ba18ff7e67d8c4da749e44dfae1d930ae5397083a51675c409049dfb626a96246c0015ca696e94ebb767a20147834bf78b07fece3f0872b057c1c519ff882501995237d8206b0b3832f78753ebd8dcbd1d3d9f5ba733538113af6b407d960ec4353c50eb38ab29888238da843cd404ed8f4952f59e4bbc0035fc77a54846a9d419179c46af1b4a3b7fc98e4d312aaa29b9b7d79e739703dc0fa41c7280d5587709277ffa11c3620f5fba985b82c238ba19b17ebd027af9424be0941719919f620dd3bb3c3f11638363708aa11f858e153cf3a69bce69978b90e4a273836100aa1e617ba455cd00426847f

View File

@ -1,3 +1,24 @@
/*
* Copyright (C) 2016 Blue Jay Wireless
* Copyright (C) 2015 Daniel Martí <mvdan@mvdan.cc>
* Copyright (C) 2014-2016 Hans-Christoph Steiner <hans@eds.org>
* Copyright (C) 2014-2016 Peter Serwylo <peter@serwylo.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.fdroid.fdroid; package org.fdroid.fdroid;
@ -8,6 +29,7 @@ import android.util.Log;
import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.App;
import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.data.RepoPushRequest;
import org.fdroid.fdroid.mock.MockRepo; import org.fdroid.fdroid.mock.MockRepo;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -82,6 +104,38 @@ public class RepoXMLHandlerTest {
}); });
} }
@Test
public void testPushRequestsRepoIgnore() {
Repo expectedRepo = new Repo();
expectedRepo.name = "non-public test repo";
expectedRepo.signingCertificate = "308204e1308202c9a0030201020204483450fa300d06092a864886f70d01010b050030213110300e060355040b1307462d44726f6964310d300b06035504031304736f7661301e170d3136303832333133333131365a170d3434303130393133333131365a30213110300e060355040b1307462d44726f6964310d300b06035504031304736f766130820222300d06092a864886f70d01010105000382020f003082020a0282020100dfdcd120f3ab224999dddf4ea33ea588d295e4d7130bef48c143e9d76e5c0e0e9e5d45e64208e35feebc79a83f08939dd6a343b7d1e2179930a105a1249ccd36d88ff3feffc6e4dc53dae0163a7876dd45ecc1ddb0adf5099aa56c1a84b52affcd45d0711ffa4de864f35ac0333ebe61ea8673eeda35a88f6af678cc4d0f80b089338ac8f2a8279a64195c611d19445cab3fd1a020afed9bd739bb95142fb2c00a8f847db5ef3325c814f8eb741bacf86ed3907bfe6e4564d2de5895df0c263824e0b75407589bae2d3a4666c13b92102d8781a8ee9bb4a5a1a78c4a9c21efdaf5584da42e84418b28f5a81d0456a3dc5b420991801e6b21e38c99bbe018a5b2d690894a114bc860d35601416aa4dc52216aff8a288d4775cddf8b72d45fd2f87303a8e9c0d67e442530be28eaf139894337266e0b33d57f949256ab32083bcc545bc18a83c9ab8247c12aea037e2b68dee31c734cb1f04f241d3b94caa3a2b258ffaf8e6eae9fbbe029a934dc0a0859c5f120334812693a1c09352340a39f2a678dbc1afa2a978bfee43afefcb7e224a58af2f3d647e5745db59061236b8af6fcfd93b3602f9e456978534f3a7851e800071bf56da80401c81d91c45f82568373af0576b1cc5eef9b85654124b6319770be3cdba3fbebe3715e8918fb6c8966624f3d0e815effac3d2ee06dd34ab9c693218b2c7c06ba99d6b74d4f17b8c3cb0203010001a321301f301d0603551d0e04160414d62bee9f3798509546acc62eb1de14b08b954d4f300d06092a864886f70d01010b05000382020100743f7c5692085895f9d1fffad390fb4202c15f123ed094df259185960fd6dadf66cb19851070f180297bba4e6996a4434616573b375cfee94fee73a4505a7ec29136b7e6c22e6436290e3686fe4379d4e3140ec6a08e70cfd3ed5b634a5eb5136efaaabf5f38e0432d3d79568a556970b8cfba2972f5d23a3856d8a981b9e9bbbbb88f35e708bde9cbc5f681cbd974085b9da28911296fe2579fa64bbe9fa0b93475a7a8db051080b0c5fade0d1c018e7858cd4cbe95145b0620e2f632cbe0f8af9cbf22e2fdaa72245ae31b0877b07181cc69dd2df74454251d8de58d25e76354abe7eb690f22e59b08795a8f2c98c578e0599503d9085927634072c82c9f82abd50fd12b8fd1a9d1954eb5cc0b4cfb5796b5aaec0356643b4a65a368442d92ef94edd3ac6a2b7fe3571b8cf9f462729228aab023ef9183f73792f5379633ccac51079177d604c6bc1873ada6f07d8da6d68c897e88a5fa5d63fdb8df820f46090e0716e7562dd3c140ba279a65b996f60addb0abe29d4bf2f5abe89480771d492307b926d91f02f341b2148502903c43d40f3c6c86a811d060711f0698b384acdcc0add44eb54e42962d3d041accc715afd49407715adc09350cb55e8d9281a3b0b6b5fcd91726eede9b7c8b13afdebb2c2b377629595f1096ba62fb14946dbac5f3c5f0b4e5b712e7acc7dcf6c46cdc5e6d6dfdeee55a0c92c2d70f080ac6";
expectedRepo.description = "This is a repository of apps to be used with F-Droid. Applications in this repository are either official binaries built by the original application developers, or are binaries built from source by the admin of f-droid.org using the tools on https://gitlab.com/u/fdroid.";
expectedRepo.timestamp = 1472071347;
RepoDetails actualDetails = getFromFile("pushRequestsIndex.xml", Repo.PUSH_REQUEST_IGNORE);
handlerTestSuite(expectedRepo, actualDetails, 2, 14, -1, 17);
checkPushRequests(actualDetails);
List<RepoPushRequest> repoPushRequests = actualDetails.repoPushRequestList;
assertNotNull(repoPushRequests);
assertEquals(0, repoPushRequests.size());
}
@Test
public void testPushRequestsRepoAlways() {
Repo expectedRepo = new Repo();
expectedRepo.name = "non-public test repo";
expectedRepo.signingCertificate = "308204e1308202c9a0030201020204483450fa300d06092a864886f70d01010b050030213110300e060355040b1307462d44726f6964310d300b06035504031304736f7661301e170d3136303832333133333131365a170d3434303130393133333131365a30213110300e060355040b1307462d44726f6964310d300b06035504031304736f766130820222300d06092a864886f70d01010105000382020f003082020a0282020100dfdcd120f3ab224999dddf4ea33ea588d295e4d7130bef48c143e9d76e5c0e0e9e5d45e64208e35feebc79a83f08939dd6a343b7d1e2179930a105a1249ccd36d88ff3feffc6e4dc53dae0163a7876dd45ecc1ddb0adf5099aa56c1a84b52affcd45d0711ffa4de864f35ac0333ebe61ea8673eeda35a88f6af678cc4d0f80b089338ac8f2a8279a64195c611d19445cab3fd1a020afed9bd739bb95142fb2c00a8f847db5ef3325c814f8eb741bacf86ed3907bfe6e4564d2de5895df0c263824e0b75407589bae2d3a4666c13b92102d8781a8ee9bb4a5a1a78c4a9c21efdaf5584da42e84418b28f5a81d0456a3dc5b420991801e6b21e38c99bbe018a5b2d690894a114bc860d35601416aa4dc52216aff8a288d4775cddf8b72d45fd2f87303a8e9c0d67e442530be28eaf139894337266e0b33d57f949256ab32083bcc545bc18a83c9ab8247c12aea037e2b68dee31c734cb1f04f241d3b94caa3a2b258ffaf8e6eae9fbbe029a934dc0a0859c5f120334812693a1c09352340a39f2a678dbc1afa2a978bfee43afefcb7e224a58af2f3d647e5745db59061236b8af6fcfd93b3602f9e456978534f3a7851e800071bf56da80401c81d91c45f82568373af0576b1cc5eef9b85654124b6319770be3cdba3fbebe3715e8918fb6c8966624f3d0e815effac3d2ee06dd34ab9c693218b2c7c06ba99d6b74d4f17b8c3cb0203010001a321301f301d0603551d0e04160414d62bee9f3798509546acc62eb1de14b08b954d4f300d06092a864886f70d01010b05000382020100743f7c5692085895f9d1fffad390fb4202c15f123ed094df259185960fd6dadf66cb19851070f180297bba4e6996a4434616573b375cfee94fee73a4505a7ec29136b7e6c22e6436290e3686fe4379d4e3140ec6a08e70cfd3ed5b634a5eb5136efaaabf5f38e0432d3d79568a556970b8cfba2972f5d23a3856d8a981b9e9bbbbb88f35e708bde9cbc5f681cbd974085b9da28911296fe2579fa64bbe9fa0b93475a7a8db051080b0c5fade0d1c018e7858cd4cbe95145b0620e2f632cbe0f8af9cbf22e2fdaa72245ae31b0877b07181cc69dd2df74454251d8de58d25e76354abe7eb690f22e59b08795a8f2c98c578e0599503d9085927634072c82c9f82abd50fd12b8fd1a9d1954eb5cc0b4cfb5796b5aaec0356643b4a65a368442d92ef94edd3ac6a2b7fe3571b8cf9f462729228aab023ef9183f73792f5379633ccac51079177d604c6bc1873ada6f07d8da6d68c897e88a5fa5d63fdb8df820f46090e0716e7562dd3c140ba279a65b996f60addb0abe29d4bf2f5abe89480771d492307b926d91f02f341b2148502903c43d40f3c6c86a811d060711f0698b384acdcc0add44eb54e42962d3d041accc715afd49407715adc09350cb55e8d9281a3b0b6b5fcd91726eede9b7c8b13afdebb2c2b377629595f1096ba62fb14946dbac5f3c5f0b4e5b712e7acc7dcf6c46cdc5e6d6dfdeee55a0c92c2d70f080ac6";
expectedRepo.description = "This is a repository of apps to be used with F-Droid. Applications in this repository are either official binaries built by the original application developers, or are binaries built from source by the admin of f-droid.org using the tools on https://gitlab.com/u/fdroid.";
expectedRepo.timestamp = 1472071347;
RepoDetails actualDetails = getFromFile("pushRequestsIndex.xml", Repo.PUSH_REQUEST_ACCEPT_ALWAYS);
handlerTestSuite(expectedRepo, actualDetails, 2, 14, -1, 17);
checkPushRequests(actualDetails);
List<RepoPushRequest> repoPushRequests = actualDetails.repoPushRequestList;
assertNotNull(repoPushRequests);
assertEquals(6, repoPushRequests.size());
}
@Test @Test
public void testMediumRepo() { public void testMediumRepo() {
Repo expectedRepo = new Repo(); Repo expectedRepo = new Repo();
@ -697,6 +751,30 @@ public class RepoXMLHandlerTest {
} }
} }
private void checkPushRequests(RepoDetails actualDetails) {
final Object[] expectedPushRequestsIndex = new Object[]{
"install", "org.fdroid.fdroid", 101002,
"install", "org.fdroid.fdroid.privileged", null,
"uninstall", "com.android.vending", null,
"uninstall", "com.facebook.orca", -12345,
"uninstall", null, null, // request with no data
"install", "asdfasdfasdf", null, // non-existent app
};
checkIncludedApps(actualDetails.apps, new String[]{
"org.fdroid.fdroid", "org.fdroid.fdroid.privileged",
});
List<RepoPushRequest> repoPushRequestList = actualDetails.repoPushRequestList;
int i = 0;
for (RepoPushRequest repoPushRequest : repoPushRequestList) {
assertEquals(repoPushRequest.request, expectedPushRequestsIndex[i]);
assertEquals(repoPushRequest.packageName, expectedPushRequestsIndex[i + 1]);
assertEquals(repoPushRequest.versionCode, expectedPushRequestsIndex[i + 2]);
i += 3;
}
}
private void handlerTestSuite(Repo expectedRepo, RepoDetails actualDetails, int appCount, int apkCount, int maxAge, int version) { private void handlerTestSuite(Repo expectedRepo, RepoDetails actualDetails, int appCount, int apkCount, int maxAge, int version) {
assertNotNull(actualDetails); assertNotNull(actualDetails);
assertFalse(TextUtils.isEmpty(actualDetails.signingCert)); assertFalse(TextUtils.isEmpty(actualDetails.signingCert));
@ -736,6 +814,7 @@ public class RepoXMLHandlerTest {
public List<Apk> apks = new ArrayList<>(); public List<Apk> apks = new ArrayList<>();
public List<App> apps = new ArrayList<>(); public List<App> apps = new ArrayList<>();
public List<RepoPushRequest> repoPushRequestList = new ArrayList<>();
@Override @Override
public void receiveRepo(String name, String description, String signingCert, int maxage, int version, long timestamp) { public void receiveRepo(String name, String description, String signingCert, int maxage, int version, long timestamp) {
@ -753,17 +832,27 @@ public class RepoXMLHandlerTest {
apps.add(app); apps.add(app);
} }
@Override
public void receiveRepoPushRequest(RepoPushRequest repoPushRequest) {
repoPushRequestList.add(repoPushRequest);
}
} }
@NonNull @NonNull
private RepoDetails getFromFile(String indexFilename) { private RepoDetails getFromFile(String indexFilename) {
return getFromFile(indexFilename, Repo.PUSH_REQUEST_IGNORE);
}
@NonNull
private RepoDetails getFromFile(String indexFilename, int pushRequests) {
try { try {
SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true); factory.setNamespaceAware(true);
SAXParser parser = factory.newSAXParser(); SAXParser parser = factory.newSAXParser();
XMLReader reader = parser.getXMLReader(); XMLReader reader = parser.getXMLReader();
RepoDetails repoDetails = new RepoDetails(); RepoDetails repoDetails = new RepoDetails();
RepoXMLHandler handler = new RepoXMLHandler(new MockRepo(100), repoDetails); MockRepo mockRepo = new MockRepo(100, pushRequests);
RepoXMLHandler handler = new RepoXMLHandler(mockRepo, repoDetails);
reader.setContentHandler(handler); reader.setContentHandler(handler);
Log.i(TAG, "test file: " + getClass().getClassLoader().getResource(indexFilename)); Log.i(TAG, "test file: " + getClass().getClassLoader().getResource(indexFilename));
InputStream input = getClass().getClassLoader().getResourceAsStream(indexFilename); InputStream input = getClass().getClassLoader().getResourceAsStream(indexFilename);

View File

@ -1,3 +1,24 @@
/*
* Copyright (C) 2016 Blue Jay Wireless
* Copyright (C) 2014-2016 Hans-Christoph Steiner <hans@eds.org>
* Copyright (C) 2014-2016 Peter Serwylo <peter@serwylo.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.fdroid.fdroid.data; package org.fdroid.fdroid.data;
import android.app.Application; import android.app.Application;
@ -93,7 +114,7 @@ public class RepoProviderTest extends FDroidProviderTest {
defaultRepos.get(i), defaultRepos.get(i),
reposFromXml[offset + 1], // address reposFromXml[offset + 1], // address
reposFromXml[offset + 2], // description reposFromXml[offset + 2], // description
Utils.calcFingerprint(reposFromXml[offset + 6]), // pubkey Utils.calcFingerprint(reposFromXml[offset + 7]), // pubkey
reposFromXml[offset] // name reposFromXml[offset] // name
); );
} }

View File

@ -4,8 +4,13 @@ import org.fdroid.fdroid.data.Repo;
public class MockRepo extends Repo { public class MockRepo extends Repo {
public MockRepo(long repoId) { public MockRepo(long id) {
id = repoId; this.id = id;
}
public MockRepo(long id, int pushRequests) {
this.id = id;
this.pushRequests = pushRequests;
} }
} }

File diff suppressed because one or more lines are too long