// Do an update from the given repo. All applications found, and their // APKs, are added to 'apps'. (If 'apps' already contains an app, its // APKs are merged into the existing one). // Returns null if successful, otherwise an error message to be displayed // to the user (if there is an interactive user!) // '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, List<DB.App> apps, StringBuilder newetag, List<Integer> keeprepos, ProgressListener progressListener) { try { int code = 0; if (repo.pubkey != null) { // This is a signed repo - we download the jar file, // check the signature, and extract the index... Log.d( "FDroid", "Getting signed index from " + repo.address + " at " + logDateFormat.format(new Date(System.currentTimeMillis()))); String address = repo.address + "/index.jar"; PackageManager pm = ctx.getPackageManager(); try { PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), 0); address += "?" + pi.versionName; } catch (Exception e) { } Bundle progressData = createProgressData(repo.address); ProgressListener.Event event = new ProgressListener.Event(RepoXMLHandler.PROGRESS_TYPE_DOWNLOAD, progressData); code = getRemoteFile( ctx, address, "tempindex.jar", repo.lastetag, newetag, progressListener, event); if (code == 200) { String jarpath = ctx.getFilesDir() + "/tempindex.jar"; JarFile jar = null; JarEntry je; Certificate[] certs; try { jar = new JarFile(jarpath, true); je = (JarEntry) jar.getEntry("index.xml"); File efile = new File(ctx.getFilesDir(), "/tempindex.xml"); InputStream input = null; OutputStream output = null; try { input = jar.getInputStream(je); output = new FileOutputStream(efile); Utils.copy(input, output); } finally { Utils.closeQuietly(output); Utils.closeQuietly(input); } certs = je.getCertificates(); } catch (SecurityException e) { Log.e("FDroid", "Invalid hash for index file"); return "Invalid hash for index file"; } finally { if (jar != null) { 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; for (Certificate cert : certs) { String certdata = Hasher.hex(cert.getEncoded()); if (repo.pubkey.equals(certdata)) { match = true; break; } } if (!match) { Log.d("FDroid", "Index signature mismatch"); return "Index signature mismatch"; } } } else { // It's an old-fashioned unsigned repo... Log.d("FDroid", "Getting unsigned index from " + repo.address); Bundle eventData = createProgressData(repo.address); ProgressListener.Event event = new ProgressListener.Event(RepoXMLHandler.PROGRESS_TYPE_DOWNLOAD, eventData); code = getRemoteFile( ctx, repo.address + "/index.xml", "tempindex.xml", repo.lastetag, newetag, progressListener, event); } if (code == 200) { // Process the index... SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser sp = spf.newSAXParser(); XMLReader xr = sp.getXMLReader(); RepoXMLHandler handler = new RepoXMLHandler(repo, apps, progressListener); xr.setContentHandler(handler); File tempIndex = new File(ctx.getFilesDir() + "/tempindex.xml"); BufferedReader r = new BufferedReader(new FileReader(tempIndex)); // A bit of a hack, this might return false positives if an apps description // or some other part of the XML file contains this, but it is a pretty good // estimate and makes the progress counter more informative. // As with asking the server about the size of the index before downloading, // this also has a time tradeoff. It takes about three seconds to iterate // through the file and count 600 apps on a slow emulator (v17), but if it is // taking two minutes to update, the three second wait may be worth it. final String APPLICATION = "<application"; handler.setTotalAppCount(Utils.countSubstringOccurrence(tempIndex, APPLICATION)); InputSource is = new InputSource(r); xr.parse(is); if (handler.pubkey != null && repo.pubkey == null) { // We read an unsigned index, but that indicates that // a signed version is now available... Log.d("FDroid", "Public key found - switching to signed repo for future updates"); repo.pubkey = handler.pubkey; try { DB db = DB.getDB(); db.updateRepoByAddress(repo); } finally { 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.id); // 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) { Log.e( "FDroid", "SSLHandShakeException updating from " + repo.address + ":\n" + Log.getStackTraceString(sslex)); return "A problem occurred while establishing an SSL connection. If this problem persists, AND you have a very old device, you could try using http instead of https for the repo URL."; } catch (Exception e) { Log.e( "FDroid", "Exception updating from " + repo.address + ":\n" + Log.getStackTraceString(e)); return "Failed to update - " + e.getMessage(); } finally { ctx.deleteFile("tempindex.xml"); ctx.deleteFile("tempindex.jar"); } return null; }
public static LessonServiceInterface getModelService() { return DB.getDB(); }