Use etags - highly experimental, especially where multiple repos are concerned
This commit is contained in:
parent
9a7d0b9f10
commit
407c903010
@ -307,7 +307,7 @@ public class AppDetails extends ListActivity {
|
|||||||
// Make sure the app is populated.
|
// Make sure the app is populated.
|
||||||
try {
|
try {
|
||||||
DB db = DB.getDB();
|
DB db = DB.getDB();
|
||||||
db.populateDetails(app);
|
db.populateDetails(app, null);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Log.d("FDroid", "Failed to populate app - " + ex.getMessage());
|
Log.d("FDroid", "Failed to populate app - " + ex.getMessage());
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -239,7 +239,7 @@ public class DB {
|
|||||||
detail_size = 0;
|
detail_size = 0;
|
||||||
apkSource = null;
|
apkSource = null;
|
||||||
added = null;
|
added = null;
|
||||||
detail_server = null;
|
server = null;
|
||||||
detail_hash = null;
|
detail_hash = null;
|
||||||
detail_hashType = null;
|
detail_hashType = null;
|
||||||
detail_permissions = null;
|
detail_permissions = null;
|
||||||
@ -250,7 +250,7 @@ public class DB {
|
|||||||
public String version;
|
public String version;
|
||||||
public int vercode;
|
public int vercode;
|
||||||
public int detail_size; // Size in bytes - 0 means we don't know!
|
public int detail_size; // Size in bytes - 0 means we don't know!
|
||||||
public String detail_server;
|
public String server;
|
||||||
public String detail_hash;
|
public String detail_hash;
|
||||||
public String detail_hashType;
|
public String detail_hashType;
|
||||||
public int minSdkVersion; // 0 if unknown
|
public int minSdkVersion; // 0 if unknown
|
||||||
@ -282,7 +282,7 @@ public class DB {
|
|||||||
|
|
||||||
public String getURL() {
|
public String getURL() {
|
||||||
String path = apkName.replace(" ", "%20");
|
String path = apkName.replace(" ", "%20");
|
||||||
return detail_server + "/" + path;
|
return server + "/" + path;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call isCompatible(apk) on an instance of this class to
|
// Call isCompatible(apk) on an instance of this class to
|
||||||
@ -364,16 +364,17 @@ public class DB {
|
|||||||
private static final String CREATE_TABLE_REPO = "create table "
|
private static final String CREATE_TABLE_REPO = "create table "
|
||||||
+ TABLE_REPO + " (" + "address text primary key, "
|
+ TABLE_REPO + " (" + "address text primary key, "
|
||||||
+ "inuse integer not null, " + "priority integer not null,"
|
+ "inuse integer not null, " + "priority integer not null,"
|
||||||
+ "pubkey text);";
|
+ "pubkey text, lastetag text);";
|
||||||
|
|
||||||
public static class Repo {
|
public static class Repo {
|
||||||
public String address;
|
public String address;
|
||||||
public boolean inuse;
|
public boolean inuse;
|
||||||
public int priority;
|
public int priority;
|
||||||
public String pubkey; // null for an unsigned repo
|
public String pubkey; // null for an unsigned repo
|
||||||
|
public String lastetag; // last etag we updated from, null forces update
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int DBVersion = 18;
|
private final int DBVersion = 19;
|
||||||
|
|
||||||
private static void createAppApk(SQLiteDatabase db) {
|
private static void createAppApk(SQLiteDatabase db) {
|
||||||
db.execSQL(CREATE_TABLE_APP);
|
db.execSQL(CREATE_TABLE_APP);
|
||||||
@ -409,6 +410,7 @@ public class DB {
|
|||||||
mContext.getString(R.string.default_repo_pubkey));
|
mContext.getString(R.string.default_repo_pubkey));
|
||||||
values.put("inuse", 1);
|
values.put("inuse", 1);
|
||||||
values.put("priority", 10);
|
values.put("priority", 10);
|
||||||
|
values.put("lastetag", (String) null);
|
||||||
db.insert(TABLE_REPO, null, values);
|
db.insert(TABLE_REPO, null, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,6 +419,8 @@ public class DB {
|
|||||||
resetTransient(db);
|
resetTransient(db);
|
||||||
if (oldVersion < 7)
|
if (oldVersion < 7)
|
||||||
db.execSQL("alter table " + TABLE_REPO + " add pubkey string");
|
db.execSQL("alter table " + TABLE_REPO + " add pubkey string");
|
||||||
|
if (oldVersion < 19)
|
||||||
|
db.execSQL("alter table " + TABLE_REPO + " add lastetag string");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -520,7 +524,9 @@ public class DB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Populate the details for the given app, if necessary.
|
// Populate the details for the given app, if necessary.
|
||||||
public void populateDetails(App app) {
|
// If 'apkrepo' is not null, only apks from that repo address are
|
||||||
|
// populated (this is used during the update process)
|
||||||
|
public void populateDetails(App app, String apkrepo) {
|
||||||
if (app.detail_Populated)
|
if (app.detail_Populated)
|
||||||
return;
|
return;
|
||||||
Cursor c = null;
|
Cursor c = null;
|
||||||
@ -538,24 +544,22 @@ public class DB {
|
|||||||
c.close();
|
c.close();
|
||||||
c = null;
|
c = null;
|
||||||
|
|
||||||
cols = new String[] { "server", "hash", "hashType", "size",
|
cols = new String[] { "hash", "hashType", "size", "permissions" };
|
||||||
"permissions" };
|
|
||||||
for (Apk apk : app.apks) {
|
for (Apk apk : app.apks) {
|
||||||
|
|
||||||
c = db.query(
|
if (apkrepo == null || apkrepo.equals(apk.server)) {
|
||||||
TABLE_APK,
|
c = db.query(TABLE_APK, cols, "id = ? and vercode = "
|
||||||
cols,
|
+ Integer.toString(apk.vercode),
|
||||||
"id = ? and vercode = " + Integer.toString(apk.vercode),
|
new String[] { apk.id }, null, null, null, null);
|
||||||
new String[] { apk.id }, null, null, null, null);
|
c.moveToFirst();
|
||||||
c.moveToFirst();
|
apk.detail_hash = c.getString(0);
|
||||||
apk.detail_server = c.getString(0);
|
apk.detail_hashType = c.getString(1);
|
||||||
apk.detail_hash = c.getString(1);
|
apk.detail_size = c.getInt(2);
|
||||||
apk.detail_hashType = c.getString(2);
|
apk.detail_permissions = CommaSeparatedList.make(c
|
||||||
apk.detail_size = c.getInt(3);
|
.getString(3));
|
||||||
apk.detail_permissions = CommaSeparatedList
|
c.close();
|
||||||
.make(c.getString(4));
|
c = null;
|
||||||
c.close();
|
}
|
||||||
c = null;
|
|
||||||
}
|
}
|
||||||
app.detail_Populated = true;
|
app.detail_Populated = true;
|
||||||
|
|
||||||
@ -636,7 +640,7 @@ public class DB {
|
|||||||
|
|
||||||
cols = new String[] { "id", "version", "vercode", "sig", "srcname",
|
cols = new String[] { "id", "version", "vercode", "sig", "srcname",
|
||||||
"apkName", "apkSource", "minSdkVersion", "added",
|
"apkName", "apkSource", "minSdkVersion", "added",
|
||||||
"features", "compatible" };
|
"features", "compatible", "server" };
|
||||||
c = db.query(TABLE_APK, cols, null, null, null, null,
|
c = db.query(TABLE_APK, cols, null, null, null, null,
|
||||||
"vercode desc");
|
"vercode desc");
|
||||||
c.moveToFirst();
|
c.moveToFirst();
|
||||||
@ -655,6 +659,7 @@ public class DB {
|
|||||||
: mDateFormat.parse(sApkAdded);
|
: mDateFormat.parse(sApkAdded);
|
||||||
apk.features = CommaSeparatedList.make(c.getString(9));
|
apk.features = CommaSeparatedList.make(c.getString(9));
|
||||||
apk.compatible = c.getInt(10) == 1;
|
apk.compatible = c.getInt(10) == 1;
|
||||||
|
apk.server = c.getString(11);
|
||||||
apps.get(apk.id).apks.add(apk);
|
apps.get(apk.id).apks.add(apk);
|
||||||
c.moveToNext();
|
c.moveToNext();
|
||||||
}
|
}
|
||||||
@ -945,7 +950,7 @@ public class DB {
|
|||||||
values.put("id", upapk.id);
|
values.put("id", upapk.id);
|
||||||
values.put("version", upapk.version);
|
values.put("version", upapk.version);
|
||||||
values.put("vercode", upapk.vercode);
|
values.put("vercode", upapk.vercode);
|
||||||
values.put("server", upapk.detail_server);
|
values.put("server", upapk.server);
|
||||||
values.put("hash", upapk.detail_hash);
|
values.put("hash", upapk.detail_hash);
|
||||||
values.put("hashType", upapk.detail_hashType);
|
values.put("hashType", upapk.detail_hashType);
|
||||||
values.put("sig", upapk.sig);
|
values.put("sig", upapk.sig);
|
||||||
@ -974,8 +979,9 @@ public class DB {
|
|||||||
Vector<Repo> repos = new Vector<Repo>();
|
Vector<Repo> repos = new Vector<Repo>();
|
||||||
Cursor c = null;
|
Cursor c = null;
|
||||||
try {
|
try {
|
||||||
c = db.rawQuery("select address, inuse, priority, pubkey from "
|
c = db.rawQuery(
|
||||||
+ TABLE_REPO + " order by priority", null);
|
"select address, inuse, priority, pubkey, lastetag from "
|
||||||
|
+ TABLE_REPO + " order by priority", null);
|
||||||
c.moveToFirst();
|
c.moveToFirst();
|
||||||
while (!c.isAfterLast()) {
|
while (!c.isAfterLast()) {
|
||||||
Repo repo = new Repo();
|
Repo repo = new Repo();
|
||||||
@ -983,6 +989,7 @@ public class DB {
|
|||||||
repo.inuse = (c.getInt(1) == 1);
|
repo.inuse = (c.getInt(1) == 1);
|
||||||
repo.priority = c.getInt(2);
|
repo.priority = c.getInt(2);
|
||||||
repo.pubkey = c.getString(3);
|
repo.pubkey = c.getString(3);
|
||||||
|
repo.lastetag = c.getString(4);
|
||||||
repos.add(repo);
|
repos.add(repo);
|
||||||
c.moveToNext();
|
c.moveToNext();
|
||||||
}
|
}
|
||||||
@ -997,7 +1004,7 @@ public class DB {
|
|||||||
|
|
||||||
public void changeServerStatus(String address) {
|
public void changeServerStatus(String address) {
|
||||||
db.execSQL("update " + TABLE_REPO
|
db.execSQL("update " + TABLE_REPO
|
||||||
+ " set inuse=1-inuse where address = ?",
|
+ " set inuse=1-inuse, lastetag=null where address = ?",
|
||||||
new String[] { address });
|
new String[] { address });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1006,6 +1013,14 @@ public class DB {
|
|||||||
values.put("inuse", repo.inuse);
|
values.put("inuse", repo.inuse);
|
||||||
values.put("priority", repo.priority);
|
values.put("priority", repo.priority);
|
||||||
values.put("pubkey", repo.pubkey);
|
values.put("pubkey", repo.pubkey);
|
||||||
|
values.put("lastetag", (String) null);
|
||||||
|
db.update(TABLE_REPO, values, "address = ?",
|
||||||
|
new String[] { repo.address });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeLastEtag(Repo repo) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put("lastetag", repo.lastetag);
|
||||||
db.update(TABLE_REPO, values, "address = ?",
|
db.update(TABLE_REPO, values, "address = ?",
|
||||||
new String[] { repo.address });
|
new String[] { repo.address });
|
||||||
}
|
}
|
||||||
@ -1016,6 +1031,7 @@ public class DB {
|
|||||||
values.put("inuse", 1);
|
values.put("inuse", 1);
|
||||||
values.put("priority", priority);
|
values.put("priority", priority);
|
||||||
values.put("pubkey", pubkey);
|
values.put("pubkey", pubkey);
|
||||||
|
values.put("lastetag", (String) null);
|
||||||
db.insert(TABLE_REPO, null, values);
|
db.insert(TABLE_REPO, null, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ public class Downloader extends Thread {
|
|||||||
// If we haven't got the apk locally, we'll have to download it...
|
// If we haven't got the apk locally, we'll have to download it...
|
||||||
String remotefile;
|
String remotefile;
|
||||||
if (curapk.apkSource == null) {
|
if (curapk.apkSource == null) {
|
||||||
remotefile = curapk.detail_server + "/" + apkname.replace(" ", "%20");
|
remotefile = curapk.server + "/" + apkname.replace(" ", "%20");
|
||||||
} else {
|
} else {
|
||||||
remotefile = curapk.apkSource;
|
remotefile = curapk.apkSource;
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
@ -237,7 +238,7 @@ public class RepoXMLHandler extends DefaultHandler {
|
|||||||
} else if (localName == "package" && curapp != null && curapk == null) {
|
} else if (localName == "package" && curapp != null && curapk == null) {
|
||||||
curapk = new DB.Apk();
|
curapk = new DB.Apk();
|
||||||
curapk.id = curapp.id;
|
curapk.id = curapp.id;
|
||||||
curapk.detail_server = server;
|
curapk.server = server;
|
||||||
hashType = null;
|
hashType = null;
|
||||||
} else if (localName == "hash" && curapk != null) {
|
} else if (localName == "hash" && curapk != null) {
|
||||||
hashType = attributes.getValue("", "type");
|
hashType = attributes.getValue("", "type");
|
||||||
@ -245,24 +246,44 @@ public class RepoXMLHandler extends DefaultHandler {
|
|||||||
curchars.setLength(0);
|
curchars.setLength(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get a remote file. Returns the HTTP response code.
|
||||||
|
// If 'etag' is not null, it's passed to the server as an If-None-Match
|
||||||
|
// header, in which case expect a 304 response if nothing changed.
|
||||||
|
// In the event of a 200 response ONLY, 'retag' (which should be passed
|
||||||
|
// empty) may contain an etag value for the response, or it may be left
|
||||||
|
// empty if none was available.
|
||||||
|
private static int getRemoteFile(Context ctx, String url, String dest,
|
||||||
|
String etag, StringBuilder retag) throws MalformedURLException,
|
||||||
|
IOException {
|
||||||
|
|
||||||
private static void getRemoteFile(Context ctx, String url, String dest)
|
URL u = new URL(url);
|
||||||
throws MalformedURLException, IOException {
|
HttpURLConnection uc = (HttpURLConnection) u.openConnection();
|
||||||
FileOutputStream f = ctx.openFileOutput(dest, Context.MODE_PRIVATE);
|
if (etag != null)
|
||||||
|
uc.setRequestProperty("If-None-Match", etag);
|
||||||
|
int code = uc.getResponseCode();
|
||||||
|
if (code == 200) {
|
||||||
|
|
||||||
BufferedInputStream getit = new BufferedInputStream(
|
FileOutputStream f = ctx.openFileOutput(dest, Context.MODE_PRIVATE);
|
||||||
new URL(url).openStream());
|
|
||||||
BufferedOutputStream bout = new BufferedOutputStream(f, 1024);
|
|
||||||
byte data[] = new byte[1024];
|
|
||||||
|
|
||||||
int readed = getit.read(data, 0, 1024);
|
BufferedInputStream getit = new BufferedInputStream(
|
||||||
while (readed != -1) {
|
new URL(url).openStream());
|
||||||
bout.write(data, 0, readed);
|
BufferedOutputStream bout = new BufferedOutputStream(f, 1024);
|
||||||
readed = getit.read(data, 0, 1024);
|
byte data[] = new byte[1024];
|
||||||
|
|
||||||
|
int readed = getit.read(data, 0, 1024);
|
||||||
|
while (readed != -1) {
|
||||||
|
bout.write(data, 0, readed);
|
||||||
|
readed = getit.read(data, 0, 1024);
|
||||||
|
}
|
||||||
|
bout.close();
|
||||||
|
getit.close();
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
String et = uc.getHeaderField("ETag");
|
||||||
|
if (et != null)
|
||||||
|
retag.append(et);
|
||||||
}
|
}
|
||||||
bout.close();
|
return code;
|
||||||
getit.close();
|
|
||||||
f.close();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,9 +292,14 @@ public class RepoXMLHandler extends DefaultHandler {
|
|||||||
// APKs are merged into the existing one).
|
// APKs are merged into the existing one).
|
||||||
// Returns null if successful, otherwise an error message to be displayed
|
// Returns null if successful, otherwise an error message to be displayed
|
||||||
// to the user (if there is an interactive user!)
|
// to the user (if there is an interactive user!)
|
||||||
public static String doUpdate(Context ctx, DB.Repo repo, Vector<DB.App> apps) {
|
// 'newetag' should be passed empty. On success, it may contain an etag
|
||||||
|
// value for the index that was successfully processed, or it may contain
|
||||||
|
// null if none was available.
|
||||||
|
public static String doUpdate(Context ctx, DB.Repo repo,
|
||||||
|
Vector<DB.App> apps, StringBuilder newetag, Vector<String> keeprepos) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
int code = 0;
|
||||||
if (repo.pubkey != null) {
|
if (repo.pubkey != null) {
|
||||||
|
|
||||||
// This is a signed repo - we download the jar file,
|
// This is a signed repo - we download the jar file,
|
||||||
@ -286,85 +312,106 @@ public class RepoXMLHandler extends DefaultHandler {
|
|||||||
address += "?" + pi.versionName;
|
address += "?" + pi.versionName;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
}
|
}
|
||||||
getRemoteFile(ctx, address, "tempindex.jar");
|
code = getRemoteFile(ctx, address, "tempindex.jar",
|
||||||
String jarpath = ctx.getFilesDir() + "/tempindex.jar";
|
repo.lastetag, newetag);
|
||||||
JarFile jar;
|
if (code == 200) {
|
||||||
JarEntry je;
|
String jarpath = ctx.getFilesDir() + "/tempindex.jar";
|
||||||
try {
|
JarFile jar;
|
||||||
jar = new JarFile(jarpath, true);
|
JarEntry je;
|
||||||
je = (JarEntry) jar.getEntry("index.xml");
|
try {
|
||||||
File efile = new File(ctx.getFilesDir(), "/tempindex.xml");
|
jar = new JarFile(jarpath, true);
|
||||||
InputStream in = new BufferedInputStream(
|
je = (JarEntry) jar.getEntry("index.xml");
|
||||||
jar.getInputStream(je), 8192);
|
File efile = new File(ctx.getFilesDir(),
|
||||||
OutputStream out = new BufferedOutputStream(
|
"/tempindex.xml");
|
||||||
new FileOutputStream(efile), 8192);
|
InputStream in = new BufferedInputStream(
|
||||||
byte[] buffer = new byte[8192];
|
jar.getInputStream(je), 8192);
|
||||||
while (true) {
|
OutputStream out = new BufferedOutputStream(
|
||||||
int nBytes = in.read(buffer);
|
new FileOutputStream(efile), 8192);
|
||||||
if (nBytes <= 0)
|
byte[] buffer = new byte[8192];
|
||||||
break;
|
while (true) {
|
||||||
out.write(buffer, 0, nBytes);
|
int nBytes = in.read(buffer);
|
||||||
|
if (nBytes <= 0)
|
||||||
|
break;
|
||||||
|
out.write(buffer, 0, nBytes);
|
||||||
|
}
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
in.close();
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
Log.e("FDroid", "Invalid hash for index file");
|
||||||
|
return "Invalid hash for index file";
|
||||||
}
|
}
|
||||||
out.flush();
|
Certificate[] certs = je.getCertificates();
|
||||||
out.close();
|
jar.close();
|
||||||
in.close();
|
if (certs == null) {
|
||||||
} catch (SecurityException e) {
|
Log.d("FDroid", "No signature found in index");
|
||||||
Log.e("FDroid", "Invalid hash for index file");
|
return "No signature found in index";
|
||||||
return "Invalid hash for index file";
|
}
|
||||||
}
|
Log.d("FDroid", "Index has " + certs.length + " signature"
|
||||||
Certificate[] certs = je.getCertificates();
|
+ (certs.length > 1 ? "s." : "."));
|
||||||
jar.close();
|
|
||||||
if (certs == null) {
|
|
||||||
Log.d("FDroid", "No signature found in index");
|
|
||||||
return "No signature found in index";
|
|
||||||
}
|
|
||||||
Log.d("FDroid", "Index has " + certs.length + " signature"
|
|
||||||
+ (certs.length > 1 ? "s." : "."));
|
|
||||||
|
|
||||||
boolean match = false;
|
boolean match = false;
|
||||||
for (Certificate cert : certs) {
|
for (Certificate cert : certs) {
|
||||||
String certdata = Hasher.hex(cert.getEncoded());
|
String certdata = Hasher.hex(cert.getEncoded());
|
||||||
if (repo.pubkey.equals(certdata)) {
|
if (repo.pubkey.equals(certdata)) {
|
||||||
match = true;
|
match = true;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!match) {
|
||||||
|
Log.d("FDroid", "Index signature mismatch");
|
||||||
|
return "Index signature mismatch";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!match) {
|
|
||||||
Log.d("FDroid", "Index signature mismatch");
|
|
||||||
return "Index signature mismatch";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// It's an old-fashioned unsigned repo...
|
// It's an old-fashioned unsigned repo...
|
||||||
Log.d("FDroid", "Getting unsigned index from " + repo.address);
|
Log.d("FDroid", "Getting unsigned index from " + repo.address);
|
||||||
getRemoteFile(ctx, repo.address + "/index.xml", "tempindex.xml");
|
code = getRemoteFile(ctx, repo.address + "/index.xml",
|
||||||
|
"tempindex.xml", repo.lastetag, newetag);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the index...
|
if (code == 200) {
|
||||||
SAXParserFactory spf = SAXParserFactory.newInstance();
|
// Process the index...
|
||||||
SAXParser sp = spf.newSAXParser();
|
SAXParserFactory spf = SAXParserFactory.newInstance();
|
||||||
XMLReader xr = sp.getXMLReader();
|
SAXParser sp = spf.newSAXParser();
|
||||||
RepoXMLHandler handler = new RepoXMLHandler(repo.address, apps);
|
XMLReader xr = sp.getXMLReader();
|
||||||
xr.setContentHandler(handler);
|
RepoXMLHandler handler = new RepoXMLHandler(repo.address, apps);
|
||||||
|
xr.setContentHandler(handler);
|
||||||
|
|
||||||
InputStreamReader isr = new FileReader(new File(ctx.getFilesDir()
|
InputStreamReader isr = new FileReader(new File(
|
||||||
+ "/tempindex.xml"));
|
ctx.getFilesDir() + "/tempindex.xml"));
|
||||||
InputSource is = new InputSource(isr);
|
InputSource is = new InputSource(isr);
|
||||||
xr.parse(is);
|
xr.parse(is);
|
||||||
|
|
||||||
if (handler.pubkey != null && repo.pubkey == null) {
|
if (handler.pubkey != null && repo.pubkey == null) {
|
||||||
// We read an unsigned index, but that indicates that
|
// We read an unsigned index, but that indicates that
|
||||||
// a signed version is now available...
|
// a signed version is now available...
|
||||||
Log.d("FDroid",
|
Log.d("FDroid",
|
||||||
"Public key found - switching to signed repo for future updates");
|
"Public key found - switching to signed repo for future updates");
|
||||||
repo.pubkey = handler.pubkey;
|
repo.pubkey = handler.pubkey;
|
||||||
DB db = DB.getDB();
|
try {
|
||||||
try {
|
DB db = DB.getDB();
|
||||||
db.updateRepoByAddress(repo);
|
db.updateRepoByAddress(repo);
|
||||||
} finally {
|
} finally {
|
||||||
DB.releaseDB();
|
DB.releaseDB();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if (code == 304) {
|
||||||
|
// The index is unchanged since we last read it. We just mark
|
||||||
|
// everything that came from this repo as being updated.
|
||||||
|
Log.d("FDroid", "Repo index for " + repo.address
|
||||||
|
+ " is up to date (by etag)");
|
||||||
|
keeprepos.add(repo.address);
|
||||||
|
// Make sure we give back the same etag. (The 200 route will
|
||||||
|
// have supplied a new one.
|
||||||
|
newetag.append(repo.lastetag);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return "Failed to read index - HTTP response "
|
||||||
|
+ Integer.toString(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (SSLHandshakeException sslex) {
|
} catch (SSLHandshakeException sslex) {
|
||||||
|
@ -102,13 +102,12 @@ public class UpdateService extends IntentService {
|
|||||||
boolean notify = prefs.getBoolean("updateNotify", false);
|
boolean notify = prefs.getBoolean("updateNotify", false);
|
||||||
|
|
||||||
// Grab some preliminary information, then we can release the
|
// Grab some preliminary information, then we can release the
|
||||||
// database
|
// database while we do all the downloading, etc...
|
||||||
// while we do all the downloading, etc...
|
|
||||||
DB db = DB.getDB();
|
|
||||||
int prevUpdates = 0;
|
int prevUpdates = 0;
|
||||||
int newUpdates = 0;
|
int newUpdates = 0;
|
||||||
Vector<DB.Repo> repos;
|
Vector<DB.Repo> repos;
|
||||||
try {
|
try {
|
||||||
|
DB db = DB.getDB();
|
||||||
repos = db.getRepos();
|
repos = db.getRepos();
|
||||||
} finally {
|
} finally {
|
||||||
DB.releaseDB();
|
DB.releaseDB();
|
||||||
@ -116,12 +115,16 @@ public class UpdateService extends IntentService {
|
|||||||
|
|
||||||
// Process each repo...
|
// Process each repo...
|
||||||
Vector<DB.App> apps = new Vector<DB.App>();
|
Vector<DB.App> apps = new Vector<DB.App>();
|
||||||
|
Vector<String> keeprepos = new Vector<String>();
|
||||||
boolean success = true;
|
boolean success = true;
|
||||||
for (DB.Repo repo : repos) {
|
for (DB.Repo repo : repos) {
|
||||||
if (repo.inuse) {
|
if (repo.inuse) {
|
||||||
|
StringBuilder newetag = new StringBuilder();
|
||||||
String err = RepoXMLHandler.doUpdate(getBaseContext(),
|
String err = RepoXMLHandler.doUpdate(getBaseContext(),
|
||||||
repo, apps);
|
repo, apps, newetag, keeprepos);
|
||||||
if (err != null) {
|
if (err == null) {
|
||||||
|
repo.lastetag = newetag.toString();
|
||||||
|
} else {
|
||||||
success = false;
|
success = false;
|
||||||
err = "Update failed for " + repo.address + " - " + err;
|
err = "Update failed for " + repo.address + " - " + err;
|
||||||
Log.d("FDroid", err);
|
Log.d("FDroid", err);
|
||||||
@ -137,16 +140,55 @@ public class UpdateService extends IntentService {
|
|||||||
Vector<DB.App> acceptedapps = new Vector<DB.App>();
|
Vector<DB.App> acceptedapps = new Vector<DB.App>();
|
||||||
Vector<DB.App> prevapps = ((FDroidApp) getApplication())
|
Vector<DB.App> prevapps = ((FDroidApp) getApplication())
|
||||||
.getApps();
|
.getApps();
|
||||||
db = DB.getDB();
|
|
||||||
|
DB db = DB.getDB();
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
// Need to flag things we're keeping despite having received
|
||||||
|
// no data about during the update. (i.e. stuff from a repo
|
||||||
|
// that we know is unchanged due to the etag)
|
||||||
|
for (String keep : keeprepos) {
|
||||||
|
for (DB.App app : prevapps) {
|
||||||
|
boolean keepapp = false;
|
||||||
|
for (DB.Apk apk : app.apks) {
|
||||||
|
if (apk.server.equals(keep)) {
|
||||||
|
keepapp = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (keepapp) {
|
||||||
|
DB.App app_k = null;
|
||||||
|
for (DB.App app2 : apps) {
|
||||||
|
if (app2.id.equals(app.id)) {
|
||||||
|
app_k = app2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (app_k == null) {
|
||||||
|
apps.add(app);
|
||||||
|
app_k = app;
|
||||||
|
}
|
||||||
|
app_k.updated = true;
|
||||||
|
if (!app_k.detail_Populated) {
|
||||||
|
db.populateDetails(app_k, keep);
|
||||||
|
}
|
||||||
|
for (DB.Apk apk : app.apks)
|
||||||
|
if (apk.server.equals(keep))
|
||||||
|
apk.updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
prevUpdates = db.beginUpdate(prevapps);
|
prevUpdates = db.beginUpdate(prevapps);
|
||||||
for (DB.App app : apps) {
|
for (DB.App app : apps) {
|
||||||
if(db.updateApplication(app))
|
if (db.updateApplication(app))
|
||||||
acceptedapps.add(app);
|
acceptedapps.add(app);
|
||||||
}
|
}
|
||||||
db.endUpdate();
|
db.endUpdate();
|
||||||
if (notify)
|
if (notify)
|
||||||
newUpdates = db.getNumUpdates();
|
newUpdates = db.getNumUpdates();
|
||||||
|
for (DB.Repo repo : repos)
|
||||||
|
db.writeLastEtag(repo);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
db.cancelUpdate();
|
db.cancelUpdate();
|
||||||
Log.e("FDroid", "Exception during update processing:\n"
|
Log.e("FDroid", "Exception during update processing:\n"
|
||||||
@ -217,7 +259,7 @@ public class UpdateService extends IntentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getIcon(DB.App app) {
|
private void getIcon(DB.App app) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@ -225,9 +267,9 @@ public class UpdateService extends IntentService {
|
|||||||
if (f.exists())
|
if (f.exists())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(app.apks.size() == 0)
|
if (app.apks.size() == 0)
|
||||||
return;
|
return;
|
||||||
String server = app.apks.get(0).detail_server;
|
String server = app.apks.get(0).server;
|
||||||
URL u = new URL(server + "/icons/" + app.icon);
|
URL u = new URL(server + "/icons/" + app.icon);
|
||||||
HttpURLConnection uc = (HttpURLConnection) u.openConnection();
|
HttpURLConnection uc = (HttpURLConnection) u.openConnection();
|
||||||
if (uc.getResponseCode() == 200) {
|
if (uc.getResponseCode() == 200) {
|
||||||
@ -251,7 +293,5 @@ public class UpdateService extends IntentService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user