From 59348f8dbdffc45573d0930b873477fe619b6b95 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 19 Mar 2015 23:17:18 +0000 Subject: [PATCH] Reseed: - Add form to manually reseed from zip or su3 URL (result status not yet working) - Add form to manually reseed from local zip or su3 file (not yet working, needs multipart/form-date moved from susimail) - Add form to create reseed zip file to share (working) - Backend support and refactoring in reseed code --- .../i2p/router/web/ConfigReseedHandler.java | 80 +++++- .../src/net/i2p/router/web/ReseedBundler.java | 163 ++++++++++++ .../net/i2p/router/web/ReseedGenerator.java | 16 ++ apps/routerconsole/jsp/configreseed.jsp | 34 ++- apps/routerconsole/jsp/createreseed.jsp | 45 ++++ history.txt | 11 + .../src/net/i2p/router/RouterVersion.java | 2 +- .../networkdb/reseed/ReseedChecker.java | 52 ++++ .../i2p/router/networkdb/reseed/Reseeder.java | 233 ++++++++++++++++-- 9 files changed, 614 insertions(+), 22 deletions(-) create mode 100644 apps/routerconsole/java/src/net/i2p/router/web/ReseedBundler.java create mode 100644 apps/routerconsole/java/src/net/i2p/router/web/ReseedGenerator.java create mode 100644 apps/routerconsole/jsp/createreseed.jsp diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigReseedHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigReseedHandler.java index d05933b82..2a5dea144 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigReseedHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigReseedHandler.java @@ -1,10 +1,16 @@ package net.i2p.router.web; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.net.URL; +import java.net.MalformedURLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import net.i2p.data.DataHelper; import net.i2p.router.networkdb.reseed.Reseeder; /** @@ -25,11 +31,72 @@ public class ConfigReseedHandler extends FormHandler { // skip the nonce checking in ReseedHandler addFormNotice(_("Starting reseed process")); } - return; - } - if (_action.equals(_("Save changes"))) { + } else if (_action.equals(_("Reseed from URL"))) { + String val = getJettyString("url"); + if (val != null) + val = val.trim(); + if (val == null || val.length() == 0) { + addFormError(_("You must enter a URL")); + return; + } + URL url; + try { + url = new URL(val); + } catch (MalformedURLException mue) { + addFormError(_("Bad URL {0}", val)); + return; + } + try { + if (!_context.netDb().reseedChecker().requestReseed(url)) { + addFormError(_("Reseeding is already in progress")); + } else { + // wait a while for completion but not forever + for (int i = 0; i < 20; i++) { + try { + Thread.sleep(1000); + } catch (InterruptedException ie) {} + if (!_context.netDb().reseedChecker().inProgress()) { + String status = _context.netDb().reseedChecker().getStatus(); + if (status.length() > 0) + addFormNotice(status); + else + addFormNotice(_("Ressed complete, check summary bar for status")); + return; + } + } + if (_context.netDb().reseedChecker().inProgress()) { + String status = _context.netDb().reseedChecker().getStatus(); + if (status.length() > 0) + addFormNotice(status); + else + addFormNotice(_("Ressed in progress, check summary bar for status")); + } + } + } catch (IllegalArgumentException iae) { + addFormError(_("Bad URL {0}", val) + " - " + iae.getMessage()); + } + } else if (_action.equals(_("Reseed from file"))) { + //////// FIXME multipart + String val = getJettyString("file"); + if (val == null || val.length() == 0) { + addFormError(_("You must enter a file")); + return; + } + ByteArrayInputStream bais = new ByteArrayInputStream(DataHelper.getASCII(val)); + try { + int count = _context.netDb().reseedChecker().requestReseed(bais); + if (count <= 0) { + addFormError(_("Reseed from file failed")); + } else { + addFormNotice(ngettext("Reseed successful, loaded {0} router info from file", + "Reseed successful, loaded {0} router infos from file", + count)); + } + } catch (IOException ioe) { + addFormError(_("Reseed from file failed") + " - " + ioe); + } + } else if (_action.equals(_("Save changes"))) { saveChanges(); - return; } //addFormError(_("Unsupported") + ' ' + _action + '.'); } @@ -84,4 +151,9 @@ public class ConfigReseedHandler extends FormHandler { else addFormError(_("Error saving the configuration (applied but not saved) - please see the error logs")); } + + /** translate (ngettext) @since 0.9.19 */ + public String ngettext(String s, String p, int n) { + return Messages.getString(n, s, p, _context); + } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ReseedBundler.java b/apps/routerconsole/java/src/net/i2p/router/web/ReseedBundler.java new file mode 100644 index 000000000..6e5f39181 --- /dev/null +++ b/apps/routerconsole/java/src/net/i2p/router/web/ReseedBundler.java @@ -0,0 +1,163 @@ +package net.i2p.router.web; +/* + * free (adj.): unencumbered; not under the control of others + * Written by jrandom in 2003 and released into the public domain + * with no warranty of any kind, either expressed or implied. + * It probably won't make your computer catch on fire, or eat + * your children, but it might. Use at your own risk. + * + */ + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import net.i2p.data.DataFormatException; +import net.i2p.data.Hash; +import net.i2p.data.router.RouterAddress; +import net.i2p.data.router.RouterInfo; +import net.i2p.router.RouterContext; + +/** + * Copy a random selection of 'count' router infos from configDir/netDb + * to 'toDir'. Skip your own router info, and old, hidden, unreachable, and + * introduced routers, and those from bad countries. + * + * Much easier than the one in installer/tools since we have a running router. + * + * Caller must delete file when done. + * + * @since 0.9.19 modified from BundleRouterInfos in installer/tools + * + */ +class ReseedBundler { + + private final RouterContext _context; + private final static String ROUTERINFO_PREFIX = "routerInfo-"; + private final static String ROUTERINFO_SUFFIX = ".dat"; + + public ReseedBundler(RouterContext ctx) { + _context = ctx; + } + + + /** + * Create a zip file with + * a random selection of 'count' router infos from configDir/netDb + * to 'toDir'. Skip your own router info, and old, hidden, unreachable, and + * introduced routers, and those from bad countries. + * + * The file will be in the temp directory. Caller must move or delete. + */ + public File createZip(int count) throws IOException { + Hash me = _context.routerHash(); + int routerCount = 0; + int copied = 0; + long tooOld = System.currentTimeMillis() - 7*24*60*60*1000L; + List infos = new ArrayList(_context.netDb().getRouters()); + // IP to router hash + Map ipMap = new HashMap(count); + List toWrite = new ArrayList(count); + Collections.shuffle(infos); + for (RouterInfo ri : infos) { + if (copied >= count) + break; + Hash key = ri.getIdentity().calculateHash(); + if (key.equals(me)) { + continue; + } + if (ri.getPublished() < tooOld) + continue; + if (ri.getCapabilities().contains("U")) + continue; + if (ri.getCapabilities().contains("K")) + continue; + Collection addrs = ri.getAddresses(); + if (addrs.isEmpty()) + continue; + + String name = getRouterInfoName(key); + boolean hasIntro = false; + boolean hasIPv4 = false; + boolean dupIP = false; + for (RouterAddress addr : addrs) { + if ("SSU".equals(addr.getTransportStyle()) && addr.getOption("ihost0") != null) { + hasIntro = true; + break; + } + String host = addr.getHost(); + if (host != null && host.contains(".")) { + hasIPv4 = true; + Hash old = ipMap.put(host, key); + if (old != null && !old.equals(key)) { + dupIP = true; + break; + } + } + } + if (dupIP) + continue; + if (hasIntro) + continue; + if (!hasIPv4) + continue; + if (_context.commSystem().isInBadCountry(ri)) + continue; + + toWrite.add(ri); + copied++; + } + + if (toWrite.isEmpty()) + throw new IOException("No router infos to include"); + + File rv = new File(_context.getTempDir(), "genreseed-" + _context.random().nextInt() + ".zip"); + ZipOutputStream zip = null; + try { + zip = new ZipOutputStream(new FileOutputStream(rv) ); + for (RouterInfo ri : toWrite) { + String name = getRouterInfoName(ri.getIdentity().calculateHash()); + ZipEntry entry = new ZipEntry(name); + entry.setTime(ri.getPublished()); + zip.putNextEntry(entry); + ri.writeBytes(zip); + } + } catch (DataFormatException dfe) { + rv.delete(); + IOException ioe = new IOException(dfe.getMessage()); + ioe.initCause(dfe); + throw ioe; + } catch (IOException ioe) { + rv.delete(); + throw ioe; + } finally { + if ( zip != null) { + try { + zip.finish(); + zip.close(); + } catch (IOException ioe) { + rv.delete(); + throw ioe; + } + } + } + return rv; + } + + /** + * Copied/modded from PersistentDataStore + */ + private static String getRouterInfoName(Hash hash) { + String b64 = hash.toBase64(); + return ROUTERINFO_PREFIX + b64 + ROUTERINFO_SUFFIX; + } +} diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ReseedGenerator.java b/apps/routerconsole/java/src/net/i2p/router/web/ReseedGenerator.java new file mode 100644 index 000000000..377f6276f --- /dev/null +++ b/apps/routerconsole/java/src/net/i2p/router/web/ReseedGenerator.java @@ -0,0 +1,16 @@ +package net.i2p.router.web; + +import java.io.File; +import java.io.IOException; + +/** + * Handler to create a i2preseed.zip file + * @since 0.9.19 + */ +public class ReseedGenerator extends HelperBase { + + public File createZip() throws IOException { + ReseedBundler rb = new ReseedBundler(_context); + return rb.createZip(100); + } +} diff --git a/apps/routerconsole/jsp/configreseed.jsp b/apps/routerconsole/jsp/configreseed.jsp index da667ace1..2e2e2f806 100644 --- a/apps/routerconsole/jsp/configreseed.jsp +++ b/apps/routerconsole/jsp/configreseed.jsp @@ -19,8 +19,40 @@ <%@include file="formhandler.jsi" %> +
+

<%=intl._("Manual Reseed from URL")%>

+

<%=intl._("Enter zip or su3 URL")%> : + +

+
+" /> +
+ +
+
+ +

<%=intl._("Manual Reseed from File")%>

+

<%=intl._("Select zip or su3 file")%> : + +

+
+" /> +
+ +
+
+

<%=intl._("Create Reseed File")%>

+

<%=intl._("Create a new reseed zip file you may share for others to reseed manually.")%> +

+
+" /> +
+ +
+
+

<%=intl._("Reseeding Configuration")%>

<%=intl._("Reseeding is the bootstrapping process used to find other routers when you first install I2P, or when your router has too few router references remaining.")%> <%=intl._("If reseeding has failed, you should first check your network connection.")%> @@ -71,7 +103,7 @@ -->

-
+
" /> " /> " /> diff --git a/apps/routerconsole/jsp/createreseed.jsp b/apps/routerconsole/jsp/createreseed.jsp new file mode 100644 index 000000000..650a237f0 --- /dev/null +++ b/apps/routerconsole/jsp/createreseed.jsp @@ -0,0 +1,45 @@ +" /><% +/* + * USE CAUTION WHEN EDITING + * Trailing whitespace OR NEWLINE on the last line will cause + * IllegalStateExceptions !!! + * + * Do not tag this file for translation. + */ +try { + java.io.InputStream in = null; + java.io.File zip = null; + try { + zip = gen.createZip(); + response.setContentLength((int) zip.length()); + long lastmod = zip.lastModified(); + if (lastmod > 0) + response.setDateHeader("Last-Modified", lastmod); + response.setDateHeader("Expires", 0); + response.addHeader("Cache-Control", "no-store, max-age=0, no-cache, must-revalidate"); + response.addHeader("Pragma", "no-cache"); + response.setContentType("application/zip; name=\"i2preseed.zip\""); + response.addHeader("Content-Disposition", "attachment; filename=\"i2preseed.zip\""); + byte buf[] = new byte[16*1024]; + in = new java.io.FileInputStream(zip); + int read = 0; + java.io.OutputStream cout = response.getOutputStream(); + while ( (read = in.read(buf)) != -1) { + cout.write(buf, 0, read); + } + } finally { + if (in != null) + try { in.close(); } catch (java.io.IOException ioe) {} + if (zip != null) + zip.delete(); + } +} catch (java.io.IOException ioe) { + // prevent 'Committed' IllegalStateException from Jetty + if (!response.isCommitted()) { + response.sendError(403, ioe.toString()); + } else { + // Jetty doesn't log this + throw ioe; + } +} +%> \ No newline at end of file diff --git a/history.txt b/history.txt index 65566854f..2e73c4370 100644 --- a/history.txt +++ b/history.txt @@ -1,9 +1,20 @@ +2015-03-19 zzz + * Reseed: + - Add form to manually reseed from zip or su3 URL + - Add form to manually reseed from local zip or su3 file + - Add form to create reseed zip file to share + - Backend support and refactoring in reseed code + 2015-03-18 zzz * NetDB: - Send exploratory lookups directly to the floodfill if we are already connected to him - Don't encrypt RI lookups when overloaded - Don't explore when overloaded + - Don't publish non-ff RI on exit if we are coming right back + * Router: Allow disabling the setting of some System properties, for embedded applications + * StatisticsManager: Publish dummy LS count if we just started + * Streaming: Reduce min RTO again * Tunnels: Drop instead of reject requests on high job lag * UPnP: Update to cyberlink 3.0 diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index c10128fe6..fecba78d6 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 6; + public final static long BUILD = 7; /** for example "-test" */ public final static String EXTRA = ""; diff --git a/router/java/src/net/i2p/router/networkdb/reseed/ReseedChecker.java b/router/java/src/net/i2p/router/networkdb/reseed/ReseedChecker.java index 2023452a8..b65bf700f 100644 --- a/router/java/src/net/i2p/router/networkdb/reseed/ReseedChecker.java +++ b/router/java/src/net/i2p/router/networkdb/reseed/ReseedChecker.java @@ -1,6 +1,9 @@ package net.i2p.router.networkdb.reseed; import java.io.File; +import java.io.InputStream; +import java.io.IOException; +import java.net.URL; import java.util.concurrent.atomic.AtomicBoolean; import net.i2p.router.RouterContext; @@ -107,6 +110,55 @@ public class ReseedChecker { } /** + * Start a reseed from a zip or su3 URI. + * + * @return true if a reseed was started, false if already in progress + * @throws IllegalArgumentException if it doesn't end with zip or su3 + * @since 0.9.19 + */ + public boolean requestReseed(URL url) throws IllegalArgumentException { + if (_inProgress.compareAndSet(false, true)) { + Reseeder reseeder = new Reseeder(_context, this); + try { + reseeder.requestReseed(url); + return true; + } catch (IllegalArgumentException iae) { + done(); + throw iae; + } catch (Throwable t) { + _log.error("Reseed failed to start", t); + done(); + return false; + } + } else { + if (_log.shouldLog(Log.WARN)) + _log.warn("Reseed already in progress"); + return false; + } + } + + /** + * Reseed from a zip or su3 input stream. Blocking. + * + * @return true if a reseed was started, false if already in progress + * @throws IOException if already in progress or on most other errors + * @since 0.9.19 + */ + public int requestReseed(InputStream in) throws IOException { + // don't really need to check for in progress here + if (_inProgress.compareAndSet(false, true)) { + try { + Reseeder reseeder = new Reseeder(_context, this); + return reseeder.requestReseed(in); + } finally { + done(); + } + } else { + throw new IOException("Reseed already in progress"); + } + } + + /** . * Is a reseed in progress? * * @since 0.9 diff --git a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java index 82328ca8c..427b67a55 100644 --- a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java +++ b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java @@ -1,9 +1,12 @@ package net.i2p.router.networkdb.reseed; +import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; +import java.io.InputStream; import java.io.IOException; +import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; @@ -130,6 +133,11 @@ public class Reseeder { _checker = rc; } + /** + * Start a reseed using the default reseed URLs. + * Supports su3 and directories. + * Threaded, nonblocking. + */ void requestReseed() { ReseedRunner reseedRunner = new ReseedRunner(); // set to daemon so it doesn't hang a shutdown @@ -137,6 +145,77 @@ public class Reseeder { reseed.start(); } + /** + * Start a reseed from a single zip or su3 URL only. + * Threaded, nonblocking. + * + * @throws IllegalArgumentException if it doesn't end with zip or su3 + * @since 0.9.19 + */ + void requestReseed(URL url) throws IllegalArgumentException { + ReseedRunner reseedRunner = new ReseedRunner(url); + // set to daemon so it doesn't hang a shutdown + Thread reseed = new I2PAppThread(reseedRunner, "Reseed", true); + reseed.start(); + } + + /** + * Start a reseed from a zip or su3 input stream. + * Blocking, inline. Should be fast. + * This will close the stream. + * + * @return number of valid routerinfos imported + * @throws IOException on most errors + * @since 0.9.19 + */ + int requestReseed(InputStream in) throws IOException { + byte[] su3Magic = DataHelper.getASCII(SU3File.MAGIC); + byte[] zipMagic = new byte[] { 0x1F, (byte) 0x8B, 0x08 }; + int len = Math.max(su3Magic.length, zipMagic.length); + byte[] magic = new byte[len]; + File tmp = null; + OutputStream out = null; + try { + DataHelper.read(in, magic); + boolean isSU3; + if (DataHelper.eq(magic, 0, su3Magic, 0, su3Magic.length)) + isSU3 = true; + else if (DataHelper.eq(magic, 0, zipMagic, 0, zipMagic.length)) + isSU3 = false; + else + throw new IOException("Not a zip or su3 file"); + tmp = new File(_context.getTempDir(), "manualreseeds-" + _context.random().nextInt() + (isSU3 ? ".su3" : ".zip")); + out = new BufferedOutputStream(new SecureFileOutputStream(tmp)); + out.write(magic); + byte buf[] = new byte[16*1024]; + int read = 0; + while ( (read = in.read(buf)) != -1) + out.write(buf, 0, read); + out.close(); + int[] stats; + ReseedRunner reseedRunner = new ReseedRunner(); + // inline + if (isSU3) + stats = reseedRunner.extractSU3(tmp); + else + stats = reseedRunner.extractZip(tmp); + int fetched = stats[0]; + int errors = stats[1]; + if (fetched <= 0) + throw new IOException("No seeds extracted"); + _checker.setStatus( + _("Reseeding: got router info from file ({0} successful, {1} errors).", fetched, errors)); + System.err.println("Reseed got " + fetched + " router infos from file with " + errors + " errors"); + _context.router().eventLog().addEvent(EventLog.RESEED, fetched + " from file"); + return fetched; + } finally { + try { in.close(); } catch (IOException ioe) {} + if (out != null) try { out.close(); } catch (IOException ioe) {} + if (tmp != null) + tmp.delete(); + } + } + private class ReseedRunner implements Runnable, EepGet.StatusListener { private boolean _isRunning; private String _proxyHost; @@ -147,8 +226,28 @@ public class Reseeder { /** bytes per sec for each su3 downloaded */ private final List _bandwidths; private static final int MAX_DATE_SETS = 2; + private final URL _url; + /** + * Start a reseed from the default URL list + */ public ReseedRunner() { + _url = null; + _bandwidths = new ArrayList(4); + } + + /** + * Start a reseed from this URL only, or null for trying one or more from the default list. + * + * @param url if non-null, must be a zip or su3 URL, NOT a directory + * @throws IllegalArgumentException if it doesn't end with zip or su3 + * @since 0.9.19 + */ + public ReseedRunner(URL url) throws IllegalArgumentException { + String lc = url.getPath().toLowerCase(Locale.US); + if (!(lc.endsWith(".zip") || lc.endsWith(".su3"))) + throw new IllegalArgumentException("Reseed URL must end with .zip or .su3"); + _url = url; _bandwidths = new ArrayList(4); } @@ -173,7 +272,18 @@ public class Reseeder { _proxyPort = _context.getProperty(PROP_PROXY_PORT, -1); } System.out.println("Reseed start"); - int total = reseed(false); + int total; + if (_url != null) { + String lc = _url.getPath().toLowerCase(Locale.US); + if (lc.endsWith(".su3")) + total = reseedSU3(_url, false); + else if (lc.endsWith(".zip")) + total = reseedZip(_url, false); + else + throw new IllegalArgumentException("Must end with .zip or .su3"); + } else { + total = reseed(false); + } if (total >= 50) { System.out.println("Reseed complete, " + total + " received"); _checker.setError(""); @@ -319,6 +429,18 @@ public class Reseeder { Collections.shuffle(URLList2, _context.random()); URLList.addAll(URLList2); } + return reseed(URLList, echoStatus); + } + + /** + * Reseed has been requested, so lets go ahead and do it. Fetch all of + * the routerInfo-*.dat files from the specified URLs + * save them into this router's netDb dir. + * + * @param echoStatus apparently always false + * @return count of routerinfos successfully fetched + */ + private int reseed(List URLList, boolean echoStatus) { int total = 0; for (int i = 0; i < URLList.size() && _isRunning; i++) { URL url = URLList.get(i); @@ -470,12 +592,38 @@ public class Reseeder { * @return count of routerinfos successfully fetched * @since 0.9.14 **/ - private int reseedSU3(URL seedURL, boolean echoStatus) { + public int reseedSU3(URL seedURL, boolean echoStatus) { + return reseedSU3OrZip(seedURL, true, echoStatus); + } + + /** + * Fetch a zip file containing routerInfo files + * + * We update the status here. + * + * @param seedURL the URL of the zip file + * @param echoStatus apparently always false + * @return count of routerinfos successfully fetched + * @since 0.9.19 + **/ + public int reseedZip(URL seedURL, boolean echoStatus) { + return reseedSU3OrZip(seedURL, false, echoStatus); + } + + /** + * Fetch an su3 or zip file containing routerInfo files + * + * We update the status here. + * + * @param seedURL the URL of the SU3 or zip file + * @param echoStatus apparently always false + * @return count of routerinfos successfully fetched + * @since 0.9.19 + **/ + private int reseedSU3OrZip(URL seedURL, boolean isSU3, boolean echoStatus) { int fetched = 0; int errors = 0; File contentRaw = null; - File zip = null; - File tmpDir = null; try { _checker.setStatus(_("Reseeding: fetching seed URL.")); System.err.println("Reseeding from " + seedURL); @@ -497,6 +645,37 @@ public class Reseeder { if (_log.shouldLog(Log.DEBUG)) _log.debug("Rcvd " + sz + " bytes in " + totalTime + " ms from " + seedURL); } + int[] stats; + if (isSU3) + stats = extractSU3(contentRaw); + else + stats = extractZip(contentRaw); + fetched = stats[0]; + errors = stats[1]; + } catch (Throwable t) { + System.err.println("Error reseeding: " + t); + _log.error("Error reseeding", t); + errors++; + } finally { + if (contentRaw != null) + contentRaw.delete(); + } + _checker.setStatus( + _("Reseeding: fetching router info from seed URL ({0} successful, {1} errors).", fetched, errors)); + System.err.println("Reseed got " + fetched + " router infos from " + seedURL + " with " + errors + " errors"); + return fetched; + } + + + /** + * @return 2 ints: number successful and number of errors + * @since 0.9.19 pulled from reseedSU3 + */ + public int[] extractSU3(File contentRaw) throws IOException { + int fetched = 0; + int errors = 0; + File zip = null; + try { SU3File su3 = new SU3File(_context, contentRaw); zip = new File(_context.getTempDir(), "reseed-" + _context.random().nextInt() + ".zip"); su3.verifyAndMigrate(zip); @@ -514,6 +693,35 @@ public class Reseeder { throw new IOException("su3 file too old"); } } catch (NumberFormatException nfe) {} + + int[] stats = extractZip(zip); + fetched = stats[0]; + errors = stats[1]; + } catch (Throwable t) { + System.err.println("Error reseeding: " + t); + _log.error("Error reseeding", t); + errors++; + } finally { + contentRaw.delete(); + if (zip != null) + zip.delete(); + } + + int[] rv = new int[2]; + rv[0] = fetched; + rv[1] = errors; + return rv; + } + + /** + * @return 2 ints: number successful and number of errors + * @since 0.9.19 pulled from reseedSU3 + */ + public int[] extractZip(File zip) throws IOException { + int fetched = 0; + int errors = 0; + File tmpDir = null; + try { tmpDir = new File(_context.getTempDir(), "reseeds-" + _context.random().nextInt()); if (!FileUtil.extractZip(zip, tmpDir)) throw new IOException("Bad zip file"); @@ -559,24 +767,17 @@ public class Reseeder { if (errors >= 5) break; } - } catch (Throwable t) { - System.err.println("Error reseeding: " + t); - _log.error("Error reseeding", t); - errors++; } finally { - if (contentRaw != null) - contentRaw.delete(); - if (zip != null) - zip.delete(); if (tmpDir != null) FileUtil.rmdir(tmpDir, false); } - _checker.setStatus( - _("Reseeding: fetching router info from seed URL ({0} successful, {1} errors).", fetched, errors)); - System.err.println("Reseed got " + fetched + " router infos from " + seedURL + " with " + errors + " errors"); + if (fetched > 0) _context.netDb().rescan(); - return fetched; + int[] rv = new int[2]; + rv[0] = fetched; + rv[1] = errors; + return rv; } /**