diff --git a/addresspublisher/build.xml b/addresspublisher/build.xml
new file mode 100644
index 0000000..f104ee3
--- /dev/null
+++ b/addresspublisher/build.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+ Builds, tests, and runs the project addresspublisher.
+
+
+
+
+
+
+ Must set build.dir
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addresspublisher/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar b/addresspublisher/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar
new file mode 100644
index 0000000..5ee71d2
Binary files /dev/null and b/addresspublisher/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar differ
diff --git a/addresspublisher/lib/i2p.jar b/addresspublisher/lib/i2p.jar
new file mode 100644
index 0000000..f04b208
Binary files /dev/null and b/addresspublisher/lib/i2p.jar differ
diff --git a/addresspublisher/lib/javax.servlet.jar b/addresspublisher/lib/javax.servlet.jar
new file mode 100644
index 0000000..08e759d
Binary files /dev/null and b/addresspublisher/lib/javax.servlet.jar differ
diff --git a/addresspublisher/lib/junit/junit-3.8.2-api.zip b/addresspublisher/lib/junit/junit-3.8.2-api.zip
new file mode 100644
index 0000000..6d792fd
Binary files /dev/null and b/addresspublisher/lib/junit/junit-3.8.2-api.zip differ
diff --git a/addresspublisher/lib/junit/junit-3.8.2.jar b/addresspublisher/lib/junit/junit-3.8.2.jar
new file mode 100644
index 0000000..d835872
Binary files /dev/null and b/addresspublisher/lib/junit/junit-3.8.2.jar differ
diff --git a/addresspublisher/lib/junit_4/junit-4.5-api.zip b/addresspublisher/lib/junit_4/junit-4.5-api.zip
new file mode 100644
index 0000000..5748c44
Binary files /dev/null and b/addresspublisher/lib/junit_4/junit-4.5-api.zip differ
diff --git a/addresspublisher/lib/junit_4/junit-4.5-src.jar b/addresspublisher/lib/junit_4/junit-4.5-src.jar
new file mode 100644
index 0000000..18774a5
Binary files /dev/null and b/addresspublisher/lib/junit_4/junit-4.5-src.jar differ
diff --git a/addresspublisher/lib/junit_4/junit-4.5.jar b/addresspublisher/lib/junit_4/junit-4.5.jar
new file mode 100644
index 0000000..83f8bc7
Binary files /dev/null and b/addresspublisher/lib/junit_4/junit-4.5.jar differ
diff --git a/addresspublisher/lib/nblibraries.properties b/addresspublisher/lib/nblibraries.properties
new file mode 100644
index 0000000..9137b06
--- /dev/null
+++ b/addresspublisher/lib/nblibraries.properties
@@ -0,0 +1,12 @@
+libs.CopyLibs.classpath=\
+ ${base}/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar
+libs.junit.classpath=\
+ ${base}/junit/junit-3.8.2.jar
+libs.junit.javadoc=\
+ ${base}/junit/junit-3.8.2-api.zip
+libs.junit_4.classpath=\
+ ${base}/junit_4/junit-4.5.jar
+libs.junit_4.javadoc=\
+ ${base}/junit_4/junit-4.5-api.zip
+libs.junit_4.src=\
+ ${base}/junit_4/junit-4.5-src.jar
diff --git a/addresspublisher/nbproject/build-impl.xml b/addresspublisher/nbproject/build-impl.xml
new file mode 100644
index 0000000..dcf3b0f
--- /dev/null
+++ b/addresspublisher/nbproject/build-impl.xml
@@ -0,0 +1,916 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set src.dir
+ Must set test.src.dir
+ Must set build.dir
+ Must set dist.dir
+ Must set build.classes.dir
+ Must set dist.javadoc.dir
+ Must set build.test.classes.dir
+ Must set build.test.results.dir
+ Must set build.classes.excludes
+ Must set dist.jar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+
+
+
+
+
+ java -cp "${run.classpath.with.dist.jar}" ${main.class}
+
+
+
+
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+ java -jar "${dist.jar.resolved}"
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+ java -jar "${dist.jar.resolved}"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set debug.class
+
+
+
+
+ Must select one file in the IDE or set debug.class
+
+
+
+
+ Must set fix.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Some tests failed; see details above.
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set test.includes
+
+
+
+ Some tests failed; see details above.
+
+
+
+
+ Must select one file in the IDE or set test.class
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addresspublisher/nbproject/genfiles.properties b/addresspublisher/nbproject/genfiles.properties
new file mode 100644
index 0000000..d9736b6
--- /dev/null
+++ b/addresspublisher/nbproject/genfiles.properties
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=00fdd0af
+build.xml.script.CRC32=3297d85f
+build.xml.stylesheet.CRC32=28e38971@1.38.2.45
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=00fdd0af
+nbproject/build-impl.xml.script.CRC32=967d9836
+nbproject/build-impl.xml.stylesheet.CRC32=f33e10ff@1.38.2.45
diff --git a/addresspublisher/nbproject/private/config.properties b/addresspublisher/nbproject/private/config.properties
new file mode 100644
index 0000000..e69de29
diff --git a/addresspublisher/nbproject/private/private.properties b/addresspublisher/nbproject/private/private.properties
new file mode 100644
index 0000000..8994e0a
--- /dev/null
+++ b/addresspublisher/nbproject/private/private.properties
@@ -0,0 +1,8 @@
+compile.on.save=true
+do.depend=false
+do.jar=true
+file.reference.i2p.jar=/mnt/bb/dream/packages/mtn/i2p.scripts/addresspublisher/lib/i2p.jar
+file.reference.javax.servlet.jar=/mnt/bb/dream/packages/mtn/i2p.scripts/addresspublisher/lib/javax.servlet.jar
+javac.debug=true
+javadoc.preview=true
+user.properties.file=/mnt/bb/dream/.netbeans/6.9/build.properties
diff --git a/addresspublisher/nbproject/private/private.xml b/addresspublisher/nbproject/private/private.xml
new file mode 100644
index 0000000..c1f155a
--- /dev/null
+++ b/addresspublisher/nbproject/private/private.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/addresspublisher/nbproject/project.properties b/addresspublisher/nbproject/project.properties
new file mode 100644
index 0000000..a8c91bc
--- /dev/null
+++ b/addresspublisher/nbproject/project.properties
@@ -0,0 +1,76 @@
+annotation.processing.enabled=true
+annotation.processing.enabled.in.editor=false
+annotation.processing.run.all.processors=true
+annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
+application.title=addresspublisher
+application.vendor=dream
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+build.generated.sources.dir=${build.dir}/generated-sources
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+# Uncomment to specify the preferred debugger connection transport:
+#debug.transport=dt_socket
+debug.classpath=\
+ ${run.classpath}
+debug.test.classpath=\
+ ${run.test.classpath}
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/addresspublisher.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+endorsed.classpath=
+excludes=
+file.reference.i2p.jar=lib/i2p.jar
+file.reference.javax.servlet.jar=lib/javax.servlet.jar
+includes=**
+jar.compress=false
+javac.classpath=\
+ ${file.reference.javax.servlet.jar}:\
+ ${file.reference.i2p.jar}
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.processorpath=\
+ ${javac.classpath}
+javac.source=1.5
+javac.target=1.5
+javac.test.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}:\
+ ${libs.junit.classpath}:\
+ ${libs.junit_4.classpath}
+javac.test.processorpath=\
+ ${javac.test.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=${source.encoding}
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+main.class=i2p.dream.addresspublisher.InspectHosts
+meta.inf.dir=${src.dir}/META-INF
+platform.active=default_platform
+run.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+# Space-separated list of JVM arguments used when running the project
+# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
+# or test-sys-prop.name=value to set system properties for unit tests):
+run.jvmargs=
+run.test.classpath=\
+ ${javac.test.classpath}:\
+ ${build.test.classes.dir}
+source.encoding=UTF-8
+src.dir=src
+test.src.dir=test
diff --git a/addresspublisher/nbproject/project.xml b/addresspublisher/nbproject/project.xml
new file mode 100644
index 0000000..c573ed6
--- /dev/null
+++ b/addresspublisher/nbproject/project.xml
@@ -0,0 +1,18 @@
+
+
+ org.netbeans.modules.java.j2seproject
+
+
+ addresspublisher
+
+
+
+
+
+
+
+
+ ./lib/nblibraries.properties
+
+
+
diff --git a/addresspublisher/src/i2p/dream/addresspublisher/Configuration.java b/addresspublisher/src/i2p/dream/addresspublisher/Configuration.java
new file mode 100644
index 0000000..1c97a97
--- /dev/null
+++ b/addresspublisher/src/i2p/dream/addresspublisher/Configuration.java
@@ -0,0 +1,27 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package i2p.dream.addresspublisher;
+
+import java.io.File;
+import java.nio.charset.Charset;
+import net.i2p.I2PAppContext;
+
+/**
+ *
+ * @author dream
+ */
+public class Configuration {
+
+ public static File getConfDir() {
+ final File confDir = new File(I2PAppContext.getGlobalContext().getConfigDir(),
+ "addressbook");
+ if(!confDir.exists())
+ confDir.mkdirs();
+ return confDir;
+ }
+
+ static public final Charset charset = Charset.forName("UTF-8");
+}
diff --git a/addresspublisher/src/i2p/dream/addresspublisher/InspectHosts.java b/addresspublisher/src/i2p/dream/addresspublisher/InspectHosts.java
new file mode 100644
index 0000000..f344fb8
--- /dev/null
+++ b/addresspublisher/src/i2p/dream/addresspublisher/InspectHosts.java
@@ -0,0 +1,142 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package i2p.dream.addresspublisher;
+
+import java.io.BufferedReader;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.i2p.I2PAppContext;
+import net.i2p.data.DataFormatException;
+import net.i2p.data.Destination;
+
+/**
+ *
+ * @author dream
+ */
+public class InspectHosts implements Runnable {
+
+ static final File hosts = new File(I2PAppContext.getGlobalContext().getConfigDir(),"hosts.txt");
+ static File getNames() {
+ return new File(Configuration.getConfDir(),"names.index");
+ }
+ Set knownHosts = new HashSet();
+
+ /* The hosts.txt file doesn't change except the end. Hosts are added at the
+ * end and not reordered or anything. Thus, hax!
+ */
+ PersistentLong lastPos = new PersistentLong(getNames());
+
+ /**
+ * @param args the command line arguments
+ */
+ public void run() {
+ FileInputStream in = null;
+ try {
+ in = new FileInputStream(getNames());
+ in.getChannel().position(lastPos.get());
+ BufferedReader r = new BufferedReader(
+ new InputStreamReader(in,Configuration.charset));
+ for(;;) {
+ String line = r.readLine();
+ if(line==null) break;
+ knownHosts.add(line.hashCode());
+ }
+ } catch(FileNotFoundException e) {
+ // index doesn't exist yet
+ } catch (IOException ex) {
+ Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
+ } finally {
+ if(in!=null) {
+ try {
+ lastPos.set(in.getChannel().position());
+ lastPos.save(getNames());
+ in.close();
+ } catch (IOException ex) {
+ Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ }
+
+ FileOutputStream out = null;
+ try {
+ in = new FileInputStream(hosts);
+ BufferedReader b = new BufferedReader(
+ new InputStreamReader(in,Configuration.charset));
+ out = new FileOutputStream(getNames(),true);
+ for(;;) {
+ String line = b.readLine();
+ if(line==null) break;
+ int comment = line.indexOf("#");
+ if(comment>0)
+ line = line.substring(0,comment)
+ .replaceAll(" ", "")
+ .replaceAll("\t", "");
+
+ if(line.length()==0) continue;
+
+ String name = line.substring(0,line.indexOf("="))
+ .replaceAll(" ", "")
+ .replaceAll("\t", "");
+
+ if(name.length()==0) continue;
+
+ if(knownHosts.contains(name.hashCode())) continue;
+
+ Record rec = RecordIndex.getInstance().newRecord();
+ rec.setName(name);
+ Destination address = new Destination();
+ try {
+ address.fromBase64(line.substring(name.length() + 1));
+ } catch (DataFormatException ex) {
+ Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
+ continue;
+ }
+ rec.setAddress(address);
+ rec.setModified(new Date());
+ try {
+ rec.maybeSave();
+ } catch (DataFormatException ex) {
+ Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
+ continue;
+ }
+ knownHosts.add(name.hashCode());
+ out.write((name+"\n")
+ .getBytes(Configuration.charset));
+
+ Logger.getLogger(InspectHosts.class.getName()).log(Level.INFO,
+ "Created new address record for {0}({1})",
+ new Object[]{name, rec.id});
+ }
+ } catch (FileNotFoundException ex) {
+ Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
+ } catch(EOFException e) {
+ // Done reading hosts.txt
+ } catch (IOException ex) {
+ Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
+ } finally {
+ try {
+ if (in != null) {
+ in.close();
+ }
+ if (out != null) {
+ out.close();
+ }
+ } catch (IOException ex) {
+ Logger.getLogger(InspectHosts.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ }
+}
diff --git a/addresspublisher/src/i2p/dream/addresspublisher/PersistentInteger.java b/addresspublisher/src/i2p/dream/addresspublisher/PersistentInteger.java
new file mode 100644
index 0000000..9175048
--- /dev/null
+++ b/addresspublisher/src/i2p/dream/addresspublisher/PersistentInteger.java
@@ -0,0 +1,65 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package i2p.dream.addresspublisher;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ *
+ * @author dream
+ */
+class PersistentInteger {
+ int num;
+ PersistentInteger(File f) throws IOException {
+ InputStream in = null;
+ try {
+ try {
+ in = new FileInputStream(f);
+ } catch (FileNotFoundException ex) {
+
+ }
+ byte[] buf = new byte[4];
+ in.read(buf);
+ num = buf[0]<<0x18 &
+ buf[1]<<0x10 &
+ buf[2]<<0x8 &
+ buf[3];
+ } finally {
+ if(in!=null) in.close();
+ }
+ }
+
+ public void save(File f) throws IOException {
+ byte[] buf = new byte[4];
+ buf[0] = (byte) (num & 0xf);
+ buf[1] = (byte) ((num >> 8) & 0xf);
+ buf[2] = (byte) ((num >> 0x10) & 0xf);
+ buf[3] = (byte) ((num >> 0x18) & 0xf);
+ OutputStream out = null;
+ try {
+ out = new FileOutputStream(f);
+ out.write(buf);
+ } finally {
+ if(out!=null)
+ out.close();
+ }
+ }
+
+ public int get() {
+ return num;
+ }
+
+ public void set(int n) {
+ num = n;
+ }
+
+}
diff --git a/addresspublisher/src/i2p/dream/addresspublisher/PersistentLong.java b/addresspublisher/src/i2p/dream/addresspublisher/PersistentLong.java
new file mode 100644
index 0000000..0b3ad7f
--- /dev/null
+++ b/addresspublisher/src/i2p/dream/addresspublisher/PersistentLong.java
@@ -0,0 +1,81 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package i2p.dream.addresspublisher;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author dream
+ */
+class PersistentLong {
+ long num;
+ PersistentLong(File f) {
+ InputStream in = null;
+ try {
+ try {
+ in = new FileInputStream(f);
+ } catch (FileNotFoundException ex) {
+
+ }
+ byte[] buf = new byte[4];
+ in.read(buf);
+ num = buf[7]<<0x38 &
+ buf[6]<<0x30 &
+ buf[5]<<0x28 &
+ buf[4]<<0x20 &
+ buf[3]<<0x18 &
+ buf[2]<<0x10 &
+ buf[1]<<0x8 &
+ buf[0];
+ } catch (IOException ex) {
+ Logger.getLogger(PersistentLong.class.getName()).log(Level.SEVERE, null, ex);
+ } finally {
+ if(in!=null) try {
+ in.close();
+ } catch (IOException ex) {
+ Logger.getLogger(PersistentLong.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ }
+
+ public void save(File f) throws IOException {
+ byte[] buf = new byte[4];
+ buf[0] = (byte) (num & 0xf);
+ buf[1] = (byte) ((num >> 8) & 0xf);
+ buf[2] = (byte) ((num >> 0x10) & 0xf);
+ buf[3] = (byte) ((num >> 0x18) & 0xf);
+ buf[4] = (byte) ((num >> 0x20) & 0xf);
+ buf[5] = (byte) ((num >> 0x28) & 0xf);
+ buf[6] = (byte) ((num >> 0x30) & 0xf);
+ buf[7] = (byte) ((num >> 0x38) & 0xf);
+ OutputStream out = null;
+ try {
+ out = new FileOutputStream(f);
+ out.write(buf);
+ } finally {
+ if(out!=null)
+ out.close();
+ }
+ }
+
+ public long get() {
+ return num;
+ }
+
+ public void set(long n) {
+ num = n;
+ }
+
+}
diff --git a/addresspublisher/src/i2p/dream/addresspublisher/Record.java b/addresspublisher/src/i2p/dream/addresspublisher/Record.java
new file mode 100644
index 0000000..0e27bf0
--- /dev/null
+++ b/addresspublisher/src/i2p/dream/addresspublisher/Record.java
@@ -0,0 +1,213 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package i2p.dream.addresspublisher;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.util.Date;
+import net.i2p.data.Destination;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.i2p.data.DataFormatException;
+
+
+class RecordCache extends LinkedHashMap {
+ // the list of records that don't get collected immediately...
+
+ private static final int MAX_ENTRIES = 10;
+
+ RecordCache() {
+ super(MAX_ENTRIES,0.75f,true);
+ }
+
+
+ @Override
+ protected boolean removeEldestEntry(Map.Entry eldest)
+ {
+ Record rec = eldest.getValue();
+ try {
+ rec.maybeSave();
+ } catch (IOException ex) {
+ Logger.getLogger(RecordCache.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (DataFormatException ex) {
+ Logger.getLogger(RecordCache.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ return size() > MAX_ENTRIES;
+ }
+}
+
+/**
+ *
+ * @author dream
+ */
+class Record {
+ final int id;
+ private boolean needsLoad = true;
+ private boolean readyToSave = false;
+ private Date modified;
+ private String name;
+ private Destination address;
+
+ protected Record(int id) {
+ this.id = id;
+ }
+
+ static File getRecordDir() {
+ File dir = new File(Configuration.getConfDir(),"records");
+ if(!dir.exists())
+ dir.mkdir();
+ return dir;
+ }
+
+ void load() {
+ if(readyToSave) {
+ try {
+ save();
+ } catch (IOException ex) {
+ Logger.getLogger(Record.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (DataFormatException ex) {
+ Logger.getLogger(Record.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ return;
+ }
+ File location = new File(getRecordDir(),Integer.toHexString(id));
+ if(!location.exists()) {
+ modified = new Date();
+ name = null;
+ address = null;
+ needsLoad = false;
+ return;
+ }
+ InputStream in = null;
+ ObjectInputStream o = null;
+ try {
+ in = new FileInputStream(location);
+ o = new ObjectInputStream(in);
+ modified = (Date) o.readObject();
+ name = (String) o.readObject();
+ address = new Destination();
+ address.readBytes(o);
+ needsLoad = false;
+ } catch (IOException ex) {
+ Logger.getLogger(Record.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (DataFormatException ex) {
+ Logger.getLogger(Record.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (ClassNotFoundException ex) {
+ Logger.getLogger(Record.class.getName()).log(Level.SEVERE, null, ex);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException ex) {
+ Logger.getLogger(Record.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ try {
+ o.close();
+ } catch (IOException ex) {
+ Logger.getLogger(Record.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ }
+
+
+ void save() throws IOException, DataFormatException {
+ File location = new File(getRecordDir(),Integer.toHexString(id));
+ OutputStream out = null;
+ try {
+ out = new FileOutputStream(location);
+ final ObjectOutputStream o = new ObjectOutputStream(out);
+ o.writeObject(modified);
+ o.writeObject(name);
+ address.writeBytes(o);
+ readyToSave = false;
+ } finally {
+ if(out!=null)
+ out.close();
+ }
+ }
+
+ Date getModified() {
+ if(needsLoad) {
+ synchronized(this) {
+ load();
+ }
+ }
+ return modified;
+ }
+
+
+ String getName() {
+ if(needsLoad) {
+ synchronized(this) {
+ load();
+ }
+ }
+ return name;
+ }
+
+ Destination getAddress() {
+ if(needsLoad) {
+ synchronized(this) {
+ load();
+ }
+ }
+ return address;
+ }
+
+ void setModified(Date d) {
+ getModified();
+ modified = d;
+ readyToSave = true;
+ }
+
+
+ void setName(String s) {
+ getName();
+ name = s;
+ readyToSave = true;
+ }
+
+ void setAddress(Destination d) {
+ getAddress();
+ address = d;
+ readyToSave = true;
+ }
+
+ static RecordCache records;
+ static public Record get(int id) {
+ if(records.containsKey(id))
+ return records.get(id);
+ Record record = new Record(id);
+ records.put(id, record);
+ return record;
+ }
+
+ void maybeSave() throws IOException, DataFormatException {
+ if(readyToSave) save();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if(o.getClass() != getClass())
+ return false;
+ Record sib = (Record) o;
+ return sib.id == id;
+ }
+
+ @Override
+ public int hashCode() {
+ return id;
+ }
+}
\ No newline at end of file
diff --git a/addresspublisher/src/i2p/dream/addresspublisher/RecordIndex.java b/addresspublisher/src/i2p/dream/addresspublisher/RecordIndex.java
new file mode 100644
index 0000000..19f0647
--- /dev/null
+++ b/addresspublisher/src/i2p/dream/addresspublisher/RecordIndex.java
@@ -0,0 +1,72 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package i2p.dream.addresspublisher;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+
+// important to go from top down to zero, so that you get the most recent
+// records to look at first, and cut off before reaching the old ones
+
+class RecordGetter implements Iterator {
+
+ int cur;
+ final int top;
+ RecordGetter(int top) {
+ this.cur = top;
+ this.top = top;
+ }
+
+ public boolean hasNext() {
+ return cur > 0;
+ }
+
+ public Record next() {
+ return Record.get(--cur);
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+}
+
+/**
+ *
+ * @author dream
+ */
+class RecordIndex implements Iterable {
+ PersistentInteger top;
+
+ RecordIndex() throws IOException {
+ top = new PersistentInteger(indexFile());
+
+ }
+
+ private File indexFile() {
+ return new File(Configuration.getConfDir(), "index");
+ }
+
+ Record newRecord() throws IOException {
+ synchronized (Record.class) {
+ top.set(top.get()+1);
+ top.save(indexFile());
+ return Record.get(top.get());
+ }
+ }
+
+
+ public Iterator iterator() {
+ return new RecordGetter(top.get());
+ }
+
+ static RecordIndex instance = null;
+ public static RecordIndex getInstance() throws IOException {
+ if(instance!=null) return instance;
+ instance = new RecordIndex();
+ return instance;
+ }
+}
diff --git a/addresspublisher/src/i2p/dream/addresspublisher/Servlet.java b/addresspublisher/src/i2p/dream/addresspublisher/Servlet.java
new file mode 100644
index 0000000..2fde037
--- /dev/null
+++ b/addresspublisher/src/i2p/dream/addresspublisher/Servlet.java
@@ -0,0 +1,121 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package i2p.dream.addresspublisher;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Date;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ *
+ * @author dream
+ */
+
+class RepeatedInspect extends Thread {
+ InspectHosts guy = new InspectHosts();
+ private boolean ready = false;
+ @Override
+ @SuppressWarnings("SleepWhileHoldingLock")
+ public void run() {
+ /* we can't wait/notify because java doesn't have inotify support, so
+ * polling is the only option.
+ * jnotify support would be really nice...
+ * at least it seeks to the lowest last position every time!
+ */
+ for(;;) {
+ try {
+ guy.run();
+ synchronized(this) {
+ ready = true;
+ this.notifyAll();
+ }
+ } catch(Exception ex) {
+ // we never want this thread to die.
+ Logger.getLogger(RepeatedInspect.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ try {
+ Thread.sleep(86400000);
+ } catch (InterruptedException ex) {
+ Logger.getLogger(RepeatedInspect.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ }
+
+ void waitUntilReady() throws InterruptedException {
+ synchronized(this) {
+ if(ready) return;
+ this.wait(10000);
+ }
+ }
+}
+
+public class Servlet extends HttpServlet {
+ String myEtag;
+ long myModified;
+ private static RepeatedInspect inspect;
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ myEtag = null;
+ myModified = 0;
+ if(inspect==null) {
+ inspect = new RepeatedInspect();
+ inspect.start();
+ }
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest request,
+ HttpServletResponse response)
+ throws ServletException, IOException
+ {
+ String etag = request.getHeader("Etag");
+ long modified;
+ if(etag!=null) {
+ modified = Integer.decode(etag).longValue();
+ } else {
+ modified = request.getDateHeader("If-Modified-Since");
+ }
+ response.setContentType("text/plain");
+ if(myEtag!=null)
+ response.setHeader("Etag",myEtag);
+ // set last modified taken care of below.
+ PrintWriter out = null;
+ try {
+ out = response.getWriter();
+ recentHosts(modified,out);
+ out.println("# done");
+ } finally {
+ if(out!=null)
+ out.close();
+ }
+
+ }
+
+ private void recentHosts(long modified, PrintWriter out) throws IOException {
+ try {
+ inspect.waitUntilReady();
+ } catch (InterruptedException ex) {
+ out.write("# not ready yet, still loading. Try again in a bit.");
+ return;
+ }
+ for(Record r : RecordIndex.getInstance()) {
+ if(r.getModified().before(new Date(modified)))
+ break;
+ out.write("# Modified="+r.getModified()
+ +"\n"+r.getName()+"="+r.getAddress().toBase64()
+ +"\n");
+ }
+ }
+
+}
diff --git a/enableAliases/nbproject/private/private.xml b/enableAliases/nbproject/private/private.xml
index c1f155a..a61defd 100644
--- a/enableAliases/nbproject/private/private.xml
+++ b/enableAliases/nbproject/private/private.xml
@@ -1,4 +1,7 @@
+
+ file:/mnt/bb/dream/packages/mtn/i2p.scripts/enableAliases/src/i2p/dream/AliasEnabler.java
+