diff --git a/build.properties b/build.properties
index 8ee39009b..7479e174e 100644
--- a/build.properties
+++ b/build.properties
@@ -91,3 +91,9 @@ javac.version=1.6
# Optional properties used in tests to enable additional tools.
#with.cobertura=/PATH/TO/cobertura.jar
#with.clover=/PATH/TO/clover.jar
+
+### Bundle router infos ###
+# Set to bundle router infos from your local I2P install in the package
+#bundle.routerInfos=true
+#bundle.routerInfos.count=200
+#bundle.routerInfos.i2pConfigDir=/PATH/TO/.i2p
diff --git a/build.xml b/build.xml
index 6c243f857..9c9d9e4a5 100644
--- a/build.xml
+++ b/build.xml
@@ -960,7 +960,7 @@
-
+
@@ -1054,6 +1054,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java
index 79117a437..f4e6767ca 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java
@@ -17,6 +17,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -25,10 +26,13 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
+import gnu.getopt.Getopt;
+
import net.i2p.data.Base64;
import net.i2p.data.DatabaseEntry;
import net.i2p.data.DataFormatException;
import net.i2p.data.Hash;
+import net.i2p.data.RouterAddress;
import net.i2p.data.RouterInfo;
import net.i2p.router.JobImpl;
import net.i2p.router.Router;
@@ -660,4 +664,161 @@ class PersistentDataStore extends TransientDataStore {
return (name.startsWith(ROUTERINFO_PREFIX.toUpperCase(Locale.US)) && name.endsWith(ROUTERINFO_SUFFIX.toUpperCase(Locale.US)));
}
}
+
+ /**
+ * Usage: PersistentDataStore -i configDir -o toDir -c count
+ *
+ * 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.
+ *
+ * Used in the build process, do not comment out.
+ *
+ * @since 0.9.15
+ */
+ public static void main(String[] args) {
+ Getopt g = new Getopt("PersistentDataStore", args, "i:o:c:");
+ String in = System.getProperty("user.home") + "/.i2p";
+ String out = "netDb";
+ int count = 200;
+ boolean error = false;
+ int c;
+ while ((c = g.getopt()) != -1) {
+ switch (c) {
+ case 'i':
+ in = g.getOptarg();
+ break;
+
+ case 'o':
+ out = g.getOptarg();
+ break;
+
+ case 'c':
+ String scount = g.getOptarg();
+ try {
+ count = Integer.parseInt(scount);
+ } catch (NumberFormatException nfe) {
+ error = true;
+ }
+ break;
+
+ case '?':
+ case ':':
+ default:
+ error = true;
+ }
+ }
+ if (error) {
+ usage();
+ System.exit(1);
+ }
+
+ File confDir = new File(in);
+ File dbDir = new File(confDir, "netDb");
+ if (!dbDir.exists()) {
+ System.out.println("NetDB directory " + dbDir + " does not exist");
+ System.exit(1);
+ }
+ File myFile = new File(confDir, "router.info");
+ File toDir = new File(out);
+ toDir.mkdirs();
+ InputStream fis = null;
+ Hash me = null;
+ try {
+ fis = new BufferedInputStream(new FileInputStream(myFile));
+ RouterInfo ri = new RouterInfo();
+ ri.readBytes(fis, true); // true = verify sig on read
+ me = ri.getIdentity().getHash();
+ } catch (Exception e) {
+ //System.out.println("Can't determine our identity");
+ } finally {
+ if (fis != null) try { fis.close(); } catch (IOException ioe) {}
+ }
+
+ int routerCount = 0;
+ List toRead = new ArrayList(2048);
+ for (int j = 0; j < B64.length(); j++) {
+ File subdir = new File(dbDir, DIR_PREFIX + B64.charAt(j));
+ File[] files = subdir.listFiles(RouterInfoFilter.getInstance());
+ if (files == null)
+ continue;
+ routerCount += files.length;
+ for (int i = 0; i < files.length; i++) {
+ toRead.add(files[i]);
+ }
+ }
+ if (toRead.isEmpty()) {
+ System.out.println("No files to copy in " + dbDir);
+ System.exit(1);
+ }
+ Collections.shuffle(toRead);
+ int copied = 0;
+ long tooOld = System.currentTimeMillis() - 7*24*60*60*1000L;
+ for (File file : toRead) {
+ if (copied >= count)
+ break;
+ Hash key = getRouterInfoHash(file.getName());
+ if (key == null) {
+ System.out.println("Skipping bad " + file);
+ continue;
+ }
+ if (key.equals(me)) {
+ System.out.println("Skipping my RI");
+ continue;
+ }
+ fis = null;
+ try {
+ fis = new BufferedInputStream(new FileInputStream(file));
+ RouterInfo ri = new RouterInfo();
+ ri.readBytes(fis, true); // true = verify sig on read
+ try { fis.close(); } catch (IOException ioe) {}
+ fis = null;
+ if (ri.getPublished() < tooOld) {
+ System.out.println("Skipping too old " + key);
+ continue;
+ }
+ if (ri.getCapabilities().contains("U")) {
+ System.out.println("Skipping unreachable " + key);
+ continue;
+ }
+ Collection addrs = ri.getAddresses();
+ if (addrs.isEmpty()) {
+ System.out.println("Skipping hidden " + key);
+ continue;
+ }
+ boolean hasIntro = false;
+ for (RouterAddress addr : addrs) {
+ if ("SSU".equals(addr.getTransportStyle()) && addr.getOption("ihost0") != null) {
+ hasIntro = true;
+ break;
+ }
+ }
+ if (hasIntro) {
+ System.out.println("Skipping introduced " + key);
+ continue;
+ }
+ File toFile = new File(toDir, file.getName());
+ // We could call ri.write() to avoid simultaneous change by the router
+ boolean ok = FileUtil.copy(file, toFile, true, true);
+ if (ok)
+ copied++;
+ else
+ System.out.println("Failed copy of " + file + " to " + toDir);
+ } catch (Exception e) {
+ System.out.println("Skipping bad " + file);
+ } finally {
+ if (fis != null) try { fis.close(); } catch (IOException ioe) {}
+ }
+ }
+ if (copied > 0) {
+ System.out.println("Copied " + copied + " router info files to " + toDir);
+ } else {
+ System.out.println("Failed to copy any files to " + toDir);
+ System.exit(1);
+ }
+ }
+
+ private static void usage() {
+ System.err.println("Usage: PersistentDataStore [-i $HOME/.i2p] [-o netDb/] [-c 200]");
+ }
}
diff --git a/router/java/src/net/i2p/router/startup/WorkingDir.java b/router/java/src/net/i2p/router/startup/WorkingDir.java
index 3763f2e7a..d44b2a539 100644
--- a/router/java/src/net/i2p/router/startup/WorkingDir.java
+++ b/router/java/src/net/i2p/router/startup/WorkingDir.java
@@ -271,6 +271,8 @@ public class WorkingDir {
// We don't currently have a default addressbook/ in the base distribution,
// but distros might put one in
"addressbook,eepsite," +
+ // 0.9.15 support bundled router infos
+ "netDb," +
// base install - files
// We don't currently have a default router.config, logger.config, susimail.config, or webapps.config in the base distribution,
// but distros might put one in