diff --git a/apps/BOB/bob.config b/apps/BOB/bob.config
new file mode 100644
index 0000000000000000000000000000000000000000..f9c28d382155eacdbf26786f47adfaa430b47cda
--- /dev/null
+++ b/apps/BOB/bob.config
@@ -0,0 +1,14 @@
+#bob.config
+#Tue Dec 30 00:00:00 UTC 2008
+# Please leave this file here for testing.
+# Thank you,
+# Sponge
+i2cp.tcp.port=7654
+BOB.host=localhost
+inbound.lengthVariance=0
+i2cp.messageReliability=BestEffort
+BOB.port=45067
+outbound.length=1
+inbound.length=1
+outbound.lengthVariance=0
+i2cp.tcp.host=localhost
diff --git a/apps/BOB/nbproject/private/private.properties b/apps/BOB/nbproject/private/private.properties
index 979533fbdbe4e008b6da2241ed3f38e46107649d..416ecb6b164c96363ca82d50579393c144f0adfc 100644
--- a/apps/BOB/nbproject/private/private.properties
+++ b/apps/BOB/nbproject/private/private.properties
@@ -1,3 +1,4 @@
+compile.on.save=false
 do.depend=false
 do.jar=true
 javac.debug=true
diff --git a/apps/BOB/nbproject/project.properties b/apps/BOB/nbproject/project.properties
index 89d7ad1da6f77c4014360127c2fb7d3d5e617311..76e318ff0a8f48eeddb6c7ca3cbdbf60bd974542 100644
--- a/apps/BOB/nbproject/project.properties
+++ b/apps/BOB/nbproject/project.properties
@@ -1,5 +1,11 @@
 application.title=BOB
 application.vendor=root
+auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs=false
+auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width=8
+auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab=8
+auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=8
+auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80
+auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=project
 build.classes.dir=${build.dir}/classes
 build.classes.excludes=**/*.java,**/*.form
 # This directory is removed when the project is cleaned:
@@ -30,13 +36,31 @@ file.reference.mstreaming.jar-1=../ministreaming/java/build/mstreaming.jar
 file.reference.NetBeansProjects-i2p.i2p=../i2p.i2p/
 file.reference.streaming.jar=../../bob/i2p/i2p.i2p/build/streaming.jar
 file.reference.streaming.jar-1=../streaming/java/build/streaming.jar
+file.reference.wrapper-freebsd=../../installer/lib/wrapper/freebsd/
+file.reference.wrapper-linux=../../installer/lib/wrapper/linux/
+file.reference.wrapper-linux64=../../installer/lib/wrapper/linux64/
+file.reference.wrapper-macosx=../../installer/lib/wrapper/macosx/
+file.reference.wrapper-solaris=../../installer/lib/wrapper/solaris/
+file.reference.wrapper-win32=../../installer/lib/wrapper/win32/
+file.reference.wrapper.jar=../../installer/lib/wrapper/linux/wrapper.jar
+file.reference.wrapper.jar-1=../../installer/lib/wrapper/freebsd/wrapper.jar
+file.reference.wrapper.jar-2=../../installer/lib/wrapper/linux64/wrapper.jar
+file.reference.wrapper.jar-3=../../installer/lib/wrapper/macosx/wrapper.jar
+file.reference.wrapper.jar-4=../../installer/lib/wrapper/solaris/wrapper.jar
+file.reference.wrapper.jar-5=../../installer/lib/wrapper/win32/wrapper.jar
 includes=**
 jar.compress=false
 javac.classpath=\
     ${file.reference.i2p.jar-1}:\
     ${file.reference.i2ptunnel.jar}:\
     ${file.reference.mstreaming.jar-1}:\
-    ${file.reference.streaming.jar-1}
+    ${file.reference.streaming.jar-1}:\
+    ${file.reference.wrapper.jar-1}:\
+    ${file.reference.wrapper.jar}:\
+    ${file.reference.wrapper.jar-2}:\
+    ${file.reference.wrapper.jar-3}:\
+    ${file.reference.wrapper.jar-4}:\
+    ${file.reference.wrapper.jar-5}
 # Space-separated list of extra javac options
 javac.compilerargs=
 javac.deprecation=false
@@ -58,6 +82,12 @@ javadoc.splitindex=true
 javadoc.use=true
 javadoc.version=false
 javadoc.windowtitle=
+jnlp.codebase.type=local
+jnlp.codebase.url=file:/root/NetBeansProjects/i2p.i2p/apps/BOB/dist/
+jnlp.descriptor=application
+jnlp.enabled=false
+jnlp.offline-allowed=false
+jnlp.signed=false
 main.class=net.i2p.BOB.Main
 manifest.file=manifest.mf
 meta.inf.dir=${src.dir}/META-INF
diff --git a/apps/BOB/src/net/i2p/BOB/BOB.java b/apps/BOB/src/net/i2p/BOB/BOB.java
index cd4ccfdcb46d9bb2a31da9f5e904d47c552735b4..2c42a9834bc7436ac4bac4669b061d6ddebb8986 100644
--- a/apps/BOB/src/net/i2p/BOB/BOB.java
+++ b/apps/BOB/src/net/i2p/BOB/BOB.java
@@ -114,6 +114,18 @@ public class BOB {
 	public final static String PROP_BOB_HOST = "BOB.host";
 	private static int maxConnections = 0;
 	private static NamedDB database;
+	private static Properties props = new Properties();
+
+
+	/**
+	 * Log a warning
+	 *
+	 * @param arg
+	 */
+	public static void info(String arg) {
+		System.out.println("INFO:" + arg);
+		_log.info(arg);
+	}
 
 	/**
 	 * Log a warning
@@ -121,7 +133,7 @@ public class BOB {
 	 * @param arg
 	 */
 	public static void warn(String arg) {
-		System.out.println(arg);
+		System.out.println("WARNING:" + arg);
 		_log.warn(arg);
 	}
 
@@ -131,7 +143,7 @@ public class BOB {
 	 * @param arg
 	 */
 	public static void error(String arg) {
-		System.out.println(arg);
+		System.out.println("ERROR: " + arg);
 		_log.error(arg);
 	}
 
@@ -147,7 +159,6 @@ public class BOB {
 		// Set up all defaults to be passed forward to other threads.
 		// Re-reading the config file in each thread is pretty damn stupid.
 		// I2PClient client = I2PClientFactory.createClient();
-		Properties props = new Properties();
 		String configLocation = System.getProperty(PROP_CONFIG_LOCATION, "bob.config");
 
 		// This is here just to ensure there is no interference with our threadgroups.
@@ -202,12 +213,12 @@ public class BOB {
 				props.store(fo, configLocation);
 				fo.close();
 			} catch(IOException ioe) {
-				warn("IOException on BOB config file " + configLocation + ", " + ioe);
+				error("IOException on BOB config file " + configLocation + ", " + ioe);
 			}
 		}
 
 		try {
-			warn("BOB is now running.");
+			info("BOB is now running.");
 			ServerSocket listener = new ServerSocket(Integer.parseInt(props.getProperty(PROP_BOB_PORT)), 10, InetAddress.getByName(props.getProperty(PROP_BOB_HOST)));
 			Socket server;
 
@@ -220,7 +231,7 @@ public class BOB {
 				t.start();
 			}
 		} catch(IOException ioe) {
-			warn("IOException on socket listen: " + ioe);
+			error("IOException on socket listen: " + ioe);
 			ioe.printStackTrace();
 		}
 	}
diff --git a/apps/BOB/src/net/i2p/BOB/DoCMDS.java b/apps/BOB/src/net/i2p/BOB/DoCMDS.java
index 2aaeba9e9c14d60eb20854b367af3785e14d490a..6033e303bf2593c000884527ed5f0fdbd4311cf7 100644
--- a/apps/BOB/src/net/i2p/BOB/DoCMDS.java
+++ b/apps/BOB/src/net/i2p/BOB/DoCMDS.java
@@ -46,7 +46,7 @@ public class DoCMDS implements Runnable {
 
 	// FIX ME
 	// I need a better way to do versioning, but this will do for now.
-	public static final String BMAJ = "00",  BMIN = "00",  BREV = "01",  BEXT = "-D";
+	public static final String BMAJ = "00",  BMIN = "00",  BREV = "03",  BEXT = "";
 	public static final String BOBversion = BMAJ + "." + BMIN + "." + BREV + BEXT;
 	private Socket server;
 	private Properties props;
@@ -89,6 +89,7 @@ public class DoCMDS implements Runnable {
 	private static final String C_setkeys = "setkeys";
 	private static final String C_setnick = "setnick";
 	private static final String C_show = "show";
+	private static final String C_show_props = "showprops";
 	private static final String C_start = "start";
 	private static final String C_status = "status";
 	private static final String C_stop = "stop";
@@ -113,32 +114,34 @@ public class DoCMDS implements Runnable {
 		{C_setkeys, C_setkeys + " BASE64_keypair * Sets the keypair for the current nickname."},
 		{C_setnick, C_setnick + " nickname * Create a new nickname."},
 		{C_show, C_show + " * Display the status of the current nickname."},
+		{C_show_props, C_show_props + " * Display the properties of the current nickname."},
 		{C_start, C_start + " * Start the current nickname tunnel."},
 		{C_status, C_status + " nickname * Display status of a nicknamed tunnel."},
 		{C_stop, C_stop + " * Stops the current nicknamed tunnel."},
 		{C_verify, C_verify + " BASE64_key * Verifies BASE64 destination."},
 		{"", "COMMANDS: " + // this is ugly, but...
-		 C_help + " " +
-		 C_clear + " " +
-		 C_getdest + " " +
-		 C_getkeys + " " +
-		 C_getnick + " " +
-		 C_inhost + " " +
-		 C_inport + " " +
-		 C_list + " " +
-		 C_newkeys + " " +
-		 C_option + " " +
-		 C_outhost + " " +
-		 C_outport + " " +
-		 C_quiet + " " +
-		 C_quit + " " +
-		 C_setkeys + " " +
-		 C_setnick + " " +
-		 C_show + " " +
-		 C_start + " " +
-		 C_status + " " +
-		 C_stop + " " +
-		 C_verify
+			C_help + " " +
+			C_clear + " " +
+			C_getdest + " " +
+			C_getkeys + " " +
+			C_getnick + " " +
+			C_inhost + " " +
+			C_inport + " " +
+			C_list + " " +
+			C_newkeys + " " +
+			C_option + " " +
+			C_outhost + " " +
+			C_outport + " " +
+			C_quiet + " " +
+			C_quit + " " +
+			C_setkeys + " " +
+			C_setnick + " " +
+			C_show + " " +
+			C_show_props + " " +
+			C_start + " " +
+			C_status + " " +
+			C_stop + " " +
+			C_verify
 		},
 		{" ", " "} // end of list
 	};
@@ -152,9 +155,10 @@ public class DoCMDS implements Runnable {
 	 */
 	DoCMDS(Socket server, Properties props, NamedDB database, Log _log) {
 		this.server = server;
-		this.props = new Properties(props);
+		this.props = new Properties();
 		this.database = database;
 		this._log = _log;
+		Lifted.copyProperties(props, this.props);
 	}
 
 	private void rlock() throws Exception {
@@ -204,17 +208,17 @@ public class DoCMDS implements Runnable {
 	private void trypnt(PrintStream out, NamedDB info, Object key) throws Exception {
 		try {
 			rlock(info);
-		} catch(Exception e) {
+		} catch (Exception e) {
 			throw new Exception(e);
 		}
 		try {
 			out.print(" " + key + ": ");
-			if(info.exists(key)) {
+			if (info.exists(key)) {
 				out.print(info.get(key));
 			} else {
 				out.print("not_set");
 			}
-		} catch(Exception e) {
+		} catch (Exception e) {
 			runlock(info);
 			throw new Exception(e);
 		}
@@ -232,13 +236,13 @@ public class DoCMDS implements Runnable {
 	private void tfpnt(PrintStream out, NamedDB info, Object key) throws Exception {
 		try {
 			rlock(info);
-		} catch(Exception e) {
+		} catch (Exception e) {
 			throw new Exception(e);
 		}
 		try {
 			out.print(" " + key + ": ");
 			out.print(info.exists(key));
-		} catch(Exception e) {
+		} catch (Exception e) {
 			runlock(info);
 			throw new Exception(e);
 		}
@@ -264,7 +268,7 @@ public class DoCMDS implements Runnable {
 	private void nickprint(PrintStream out, NamedDB info) throws Exception {
 		try {
 			rlock(info);
-		} catch(Exception e) {
+		} catch (Exception e) {
 			throw new Exception(e);
 		}
 		try {
@@ -280,7 +284,32 @@ public class DoCMDS implements Runnable {
 			trypnt(out, info, P_OUTPORT);
 			trypnt(out, info, P_OUTHOST);
 			out.println();
-		} catch(Exception e) {
+		} catch (Exception e) {
+			runlock(info);
+			throw new Exception(e);
+		}
+
+		runlock(info);
+	}
+
+	/**
+	 * Dump properties information from the database
+	 *
+	 * @param out
+	 * @param info
+	 * @throws Exception
+	 */
+	private void propprint(PrintStream out, NamedDB info) throws Exception {
+		try {
+			rlock(info);
+		} catch (Exception e) {
+			throw new Exception(e);
+		}
+		try {
+
+			trypnt(out, info, P_PROPERTIES);
+			out.println();
+		} catch (Exception e) {
 			runlock(info);
 			throw new Exception(e);
 		}
@@ -297,16 +326,16 @@ public class DoCMDS implements Runnable {
 	private void ttlpnt(PrintStream out, Object Arg) throws Exception {
 		try {
 			database.getReadLock();
-		} catch(Exception e) {
+		} catch (Exception e) {
 			throw new Exception(e);
 		}
 
 		try {
-			if(database.exists(Arg)) {
+			if (database.exists(Arg)) {
 				out.print("DATA");
-				nickprint(out, (NamedDB)database.get(Arg));
+				nickprint(out, (NamedDB) database.get(Arg));
 			}
-		} catch(Exception e) {
+		} catch (Exception e) {
 			database.releaseReadLock();
 			throw new Exception(e);
 		}
@@ -325,7 +354,7 @@ public class DoCMDS implements Runnable {
 		boolean retval;
 		try {
 			rlock(Arg);
-		} catch(Exception e) {
+		} catch (Exception e) {
 			throw new Exception(e);
 		}
 
@@ -333,7 +362,7 @@ public class DoCMDS implements Runnable {
 			retval = (Arg.get(P_STARTING).equals(Boolean.TRUE) ||
 				Arg.get(P_STOPPING).equals(Boolean.TRUE) ||
 				Arg.get(P_RUNNING).equals(Boolean.TRUE));
-		} catch(Exception e) {
+		} catch (Exception e) {
 			runlock();
 			throw new Exception(e);
 		}
@@ -352,7 +381,7 @@ public class DoCMDS implements Runnable {
 		try {
 			Destination x = new Destination(data);
 			return true;
-		} catch(Exception e) {
+		} catch (Exception e) {
 			return false;
 		}
 	}
@@ -369,50 +398,52 @@ public class DoCMDS implements Runnable {
 			// Get input from the client
 			BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));
 			PrintStream out = new PrintStream(server.getOutputStream());
-quit:                   {
-die:                            {
+			quit:
+			{
+				die:
+				{
 					prikey = new ByteArrayOutputStream();
 					out.println("BOB " + BOBversion);
 					out.println("OK");
-					while((line = in.readLine()) != null) {
+					while ((line = in.readLine()) != null) {
 						StringTokenizer token = new StringTokenizer(line, " "); // use a space as a delimiter
 						String Command = "";
 						String Arg = "";
 						NamedDB info;
 
-						if(token.countTokens() != 0) {
+						if (token.countTokens() != 0) {
 							Command = token.nextToken();
 							Command =
 								Command.toLowerCase();
-							if(token.countTokens() != 0) {
+							if (token.countTokens() != 0) {
 								Arg = token.nextToken();
 							} else {
 								Arg = "";
 							}
 							// The rest of the tokens are considered junk,
 							// and discarded without any warnings.
-							if(Command.equals(C_help)) {
-								for(int i = 0; !C_ALL[i][0].equals(" "); i++) {
-									if(C_ALL[i][0].equalsIgnoreCase(Arg)) {
+							if (Command.equals(C_help)) {
+								for (int i = 0; !C_ALL[i][0].equals(" "); i++) {
+									if (C_ALL[i][0].equalsIgnoreCase(Arg)) {
 										out.println("OK " + C_ALL[i][1]);
 									}
 
 								}
-							} else if(Command.equals(C_getdest)) {
-								if(ns) {
-									if(dk) {
+							} else if (Command.equals(C_getdest)) {
+								if (ns) {
+									if (dk) {
 										try {
 											rlock();
-										} catch(Exception ex) {
+										} catch (Exception ex) {
 											break die;
 										}
 
 										try {
 											out.println("OK " + nickinfo.get(P_DEST));
-										} catch(Exception e) {
+										} catch (Exception e) {
 											try {
 												runlock();
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												break die;
 											}
 											break die;
@@ -420,7 +451,7 @@ die:                            {
 
 										try {
 											runlock();
-										} catch(Exception ex) {
+										} catch (Exception ex) {
 											break die;
 										}
 
@@ -432,31 +463,31 @@ die:                            {
 									nns(out);
 								}
 
-							} else if(Command.equals(C_list)) {
+							} else if (Command.equals(C_list)) {
 								// Produce a formatted list of all nicknames
 								database.getReadLock();
-								for(int i = 0; i <
+								for (int i = 0; i <
 									database.getcount(); i++) {
 									try {
-										info = (NamedDB)database.getnext(i);
+										info = (NamedDB) database.getnext(i);
 										out.print("DATA");
-									} catch(Exception e) {
+									} catch (Exception e) {
 										database.releaseReadLock();
 										break die;
 									}
 
 									try {
 										info.getReadLock();
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 									try {
 										nickprint(out, info);
-									} catch(Exception e) {
+									} catch (Exception e) {
 										try {
 											info.releaseReadLock();
 											database.releaseReadLock();
-										} catch(Exception ex) {
+										} catch (Exception ex) {
 											break die;
 										}
 										break die;
@@ -464,24 +495,24 @@ die:                            {
 
 									try {
 										info.releaseReadLock();
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 								}
 
 								try {
 									database.releaseReadLock();
-								} catch(Exception ex) {
+								} catch (Exception ex) {
 									break die;
 								}
 								out.println("OK Listing done");
-							} else if(Command.equals(C_quit)) {
+							} else if (Command.equals(C_quit)) {
 								// End the command session
 								break quit;
-							} else if(Command.equals(C_newkeys)) {
-								if(ns) {
+							} else if (Command.equals(C_newkeys)) {
+								if (ns) {
 									try {
-										if(tunnelactive(nickinfo)) {
+										if (tunnelactive(nickinfo)) {
 											out.println("ERROR tunnel is active");
 										} else {
 											try {
@@ -490,17 +521,17 @@ die:                            {
 												d = I2PClientFactory.createClient().createDestination(prikey);
 												try {
 													wlock();
-												} catch(Exception e) {
+												} catch (Exception e) {
 													break die;
 												}
 
 												try {
 													nickinfo.add(P_KEYS, prikey.toByteArray());
 													nickinfo.add(P_DEST, d.toBase64());
-												} catch(Exception e) {
+												} catch (Exception e) {
 													try {
 														wunlock();
-													} catch(Exception ex) {
+													} catch (Exception ex) {
 														break die;
 													}
 													break die;
@@ -509,68 +540,68 @@ die:                            {
 												dk = true;
 												try {
 													wunlock();
-												} catch(Exception ex) {
+												} catch (Exception ex) {
 													break die;
 												}
 
 												try {
 													rlock();
-												} catch(Exception ex) {
+												} catch (Exception ex) {
 													break die;
 												}
 
 												try {
 													out.println("OK " + nickinfo.get(P_DEST));
-												} catch(Exception e) {
+												} catch (Exception e) {
 													runlock();
 													break die;
 												}
 
 												try {
 													runlock();
-												} catch(Exception ex) {
+												} catch (Exception ex) {
 													break die;
 												}
-											} catch(I2PException ipe) {
+											} catch (I2PException ipe) {
 												BOB.error("Error generating keys" + ipe);
 												out.println("ERROR generating keys");
 											}
 
 										}
-									} catch(Exception e) {
+									} catch (Exception e) {
 										break die;
 									}
 
 								} else {
 									try {
 										nns(out);
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 								}
 
-							} else if(Command.equals(C_getkeys)) {
+							} else if (Command.equals(C_getkeys)) {
 								// Return public key
-								if(dk) {
+								if (dk) {
 									prikey = new ByteArrayOutputStream();
 									try {
 										rlock();
-									} catch(Exception e) {
+									} catch (Exception e) {
 										break die;
 									}
 									try {
-										prikey.write(((byte[])nickinfo.get(P_KEYS)));
-									} catch(Exception ex) {
+										prikey.write(((byte[]) nickinfo.get(P_KEYS)));
+									} catch (Exception ex) {
 										try {
 											runlock();
-										} catch(Exception ee) {
+										} catch (Exception ee) {
 											break die;
 										}
 										break die;
 									}
 									try {
 										runlock();
-									} catch(Exception e) {
+									} catch (Exception e) {
 										break die;
 									}
 
@@ -579,23 +610,23 @@ die:                            {
 									out.println("ERROR no public key has been set");
 								}
 
-							} else if(Command.equals(C_quiet)) {
-								if(ns) {
+							} else if (Command.equals(C_quiet)) {
+								if (ns) {
 									try {
-										if(tunnelactive(nickinfo)) {
+										if (tunnelactive(nickinfo)) {
 											out.println("ERROR tunnel is active");
 										} else {
 											try {
 												wlock();
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												break die;
 											}
 											try {
 												nickinfo.add(P_QUIET, new Boolean(Boolean.parseBoolean(Arg) == true));
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												try {
 													wunlock();
-												} catch(Exception ee) {
+												} catch (Exception ee) {
 													break die;
 												}
 												break die;
@@ -603,59 +634,59 @@ die:                            {
 
 											try {
 												wunlock();
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												break die;
 											}
 
 											out.println("OK Quiet set");
 										}
 
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 
 								} else {
 									try {
 										nns(out);
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 								}
 
-							}else if(Command.equals(C_verify)) {
-								if(is64ok(Arg)) {
+							} else if (Command.equals(C_verify)) {
+								if (is64ok(Arg)) {
 									out.println("OK");
 								} else {
 									out.println("ERROR not in BASE64 format");
 								}
-							} else if(Command.equals(C_setkeys)) {
+							} else if (Command.equals(C_setkeys)) {
 								// Set the NamedDB to a privatekey in BASE64 format
-								if(ns) {
+								if (ns) {
 									try {
-										if(tunnelactive(nickinfo)) {
+										if (tunnelactive(nickinfo)) {
 											out.println("ERROR tunnel is active");
 										} else {
 											try {
 												prikey = new ByteArrayOutputStream();
 												prikey.write(net.i2p.data.Base64.decode(Arg));
 												d.fromBase64(Arg);
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												Arg = "";
 											}
 
-											if((Arg.length() == 884) && is64ok(Arg)) {
+											if ((Arg.length() == 884) && is64ok(Arg)) {
 												try {
 													wlock();
-												} catch(Exception ex) {
+												} catch (Exception ex) {
 													break die;
 												}
 												try {
 													nickinfo.add(P_KEYS, prikey.toByteArray());
 													nickinfo.add(P_DEST, d.toBase64());
-												} catch(Exception ex) {
+												} catch (Exception ex) {
 													try {
 														wunlock();
-													} catch(Exception ee) {
+													} catch (Exception ee) {
 														break die;
 													}
 													break die;
@@ -663,22 +694,22 @@ die:                            {
 												dk = true;
 												try {
 													wunlock();
-												} catch(Exception ex) {
+												} catch (Exception ex) {
 													break die;
 												}
 
 												try {
 													rlock();
-												} catch(Exception ex) {
+												} catch (Exception ex) {
 													break die;
 												}
 
 												try {
 													out.println("OK " + nickinfo.get(P_DEST));
-												} catch(Exception e) {
+												} catch (Exception e) {
 													try {
 														runlock();
-													} catch(Exception ex) {
+													} catch (Exception ex) {
 														break die;
 													}
 													break die;
@@ -686,7 +717,7 @@ die:                            {
 
 												try {
 													runlock();
-												} catch(Exception ex) {
+												} catch (Exception ex) {
 													break die;
 												}
 											} else {
@@ -694,34 +725,34 @@ die:                            {
 											}
 
 										}
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 
 								} else {
 									try {
 										nns(out);
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 								}
 
-							} else if(Command.equals(C_setnick)) {
+							} else if (Command.equals(C_setnick)) {
 								ns = dk = ip = op = false;
 								try {
 									database.getReadLock();
-								} catch(Exception ex) {
+								} catch (Exception ex) {
 									break die;
 								}
 								try {
-									nickinfo = (NamedDB)database.get(Arg);
-									if(!tunnelactive(nickinfo)) {
+									nickinfo = (NamedDB) database.get(Arg);
+									if (!tunnelactive(nickinfo)) {
 										nickinfo = null;
 										ns =
 											true;
 									}
 
-								} catch(Exception b) {
+								} catch (Exception b) {
 									nickinfo = null;
 									ns =
 										true;
@@ -729,15 +760,15 @@ die:                            {
 
 								try {
 									database.releaseReadLock();
-								} catch(Exception ex) {
+								} catch (Exception ex) {
 									break die;
 								}
 								// Clears and Sets the initial NamedDB structure to work with
-								if(ns) {
+								if (ns) {
 									nickinfo = new NamedDB();
 									try {
 										wlock();
-									} catch(Exception e) {
+									} catch (Exception e) {
 										break die;
 									}
 
@@ -750,22 +781,23 @@ die:                            {
 										nickinfo.add(P_QUIET, Boolean.FALSE);
 										nickinfo.add(P_INHOST, "localhost");
 										nickinfo.add(P_OUTHOST, "localhost");
-										Properties Q = new Properties(props);
+										Properties Q = new Properties();
+										Lifted.copyProperties(this.props, Q);
 										Q.setProperty("inbound.nickname", Arg);
 										Q.setProperty("outbound.nickname", Arg);
 										nickinfo.add(P_PROPERTIES, Q);
-									} catch(Exception e) {
+									} catch (Exception e) {
 										try {
 											wunlock();
 											break die;
-										} catch(Exception ee) {
+										} catch (Exception ee) {
 											break die;
 										}
 
 									}
 									try {
 										wunlock();
-									} catch(Exception e) {
+									} catch (Exception e) {
 										break die;
 									}
 
@@ -774,51 +806,51 @@ die:                            {
 									out.println("ERROR tunnel is active");
 								}
 
-							} else if(Command.equals(C_option)) {
-								if(ns) {
+							} else if (Command.equals(C_option)) {
+								if (ns) {
 									try {
-										if(tunnelactive(nickinfo)) {
+										if (tunnelactive(nickinfo)) {
 											out.println("ERROR tunnel is active");
 										} else {
 											StringTokenizer otoken = new StringTokenizer(Arg, "="); // use an equal sign as a delimiter
-											if(otoken.countTokens() != 2) {
+											if (otoken.countTokens() != 2) {
 												out.println("ERROR to many or no options.");
 											} else {
 												String pname = otoken.nextToken();
 												String pval = otoken.nextToken();
 												try {
 													rlock();
-												} catch(Exception ex) {
+												} catch (Exception ex) {
 													break die;
 												}
 
-												Properties Q = (Properties)nickinfo.get(P_PROPERTIES);
+												Properties Q = (Properties) nickinfo.get(P_PROPERTIES);
 												try {
 													runlock();
-												} catch(Exception ex) {
+												} catch (Exception ex) {
 													break die;
 												}
 
 												Q.setProperty(pname, pval);
 												try {
 													wlock();
-												} catch(Exception ex) {
+												} catch (Exception ex) {
 													break die;
 												}
 
 												try {
 													nickinfo.add(P_PROPERTIES, Q);
-												} catch(Exception ex) {
+												} catch (Exception ex) {
 													try {
 														wunlock();
-													} catch(Exception ee) {
+													} catch (Exception ee) {
 														break die;
 													}
 													break die;
 												}
 												try {
 													wunlock();
-												} catch(Exception ex) {
+												} catch (Exception ex) {
 													break die;
 												}
 
@@ -826,7 +858,7 @@ die:                            {
 											}
 
 										}
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 
@@ -834,23 +866,23 @@ die:                            {
 									nns(out);
 								}
 
-							} else if(Command.equals(C_getnick)) {
+							} else if (Command.equals(C_getnick)) {
 								// Get the NamedDB to work with...
 								try {
 									database.getReadLock();
-								} catch(Exception ex) {
+								} catch (Exception ex) {
 									break die;
 								}
 								try {
-									nickinfo = (NamedDB)database.get(Arg);
+									nickinfo = (NamedDB) database.get(Arg);
 									ns = true;
-								} catch(RuntimeException b) {
+								} catch (RuntimeException b) {
 									try {
 										nns(out);
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										try {
 											database.releaseReadLock();
-										} catch(Exception ee) {
+										} catch (Exception ee) {
 											break die;
 										}
 										break die;
@@ -858,54 +890,54 @@ die:                            {
 								}
 
 								database.releaseReadLock();
-								if(ns) {
+								if (ns) {
 									try {
 										rlock();
-									} catch(Exception e) {
+									} catch (Exception e) {
 										break die;
 									}
 									try {
 										dk = nickinfo.exists(P_KEYS);
 										ip = nickinfo.exists(P_INPORT);
 										op = nickinfo.exists(P_OUTPORT);
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										try {
 											runlock();
-										} catch(Exception ee) {
+										} catch (Exception ee) {
 											break die;
 										}
 										break die;
 									}
 									try {
 										runlock();
-									} catch(Exception e) {
+									} catch (Exception e) {
 										break die;
 									}
 // Finally say OK.
 									out.println("OK Nickname set to " + Arg);
 								}
 
-							} else if(Command.equals(C_inport)) {
+							} else if (Command.equals(C_inport)) {
 								// Set the NamedDB inbound TO the router port
 								// app --> BOB
-								if(ns) {
+								if (ns) {
 									try {
-										if(tunnelactive(nickinfo)) {
+										if (tunnelactive(nickinfo)) {
 											out.println("ERROR tunnel is active");
 										} else {
 											int prt;
 											try {
 												wlock();
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												break die;
 											}
 
 											try {
 												nickinfo.kill(P_INPORT);
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												try {
 													wunlock();
-												} catch(Exception ee) {
+												} catch (Exception ee) {
 													break die;
 												}
 
@@ -913,13 +945,13 @@ die:                            {
 											}
 											try {
 												prt = Integer.parseInt(Arg);
-												if(prt > 1 && prt < 65536) {
+												if (prt > 1 && prt < 65536) {
 													try {
 														nickinfo.add(P_INPORT, new Integer(prt));
-													} catch(Exception ex) {
+													} catch (Exception ex) {
 														try {
 															wunlock();
-														} catch(Exception ee) {
+														} catch (Exception ee) {
 															break die;
 														}
 
@@ -927,45 +959,45 @@ die:                            {
 													}
 												}
 
-											} catch(NumberFormatException nfe) {
+											} catch (NumberFormatException nfe) {
 												out.println("ERROR not a number");
 											}
 
 											try {
 												wunlock();
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												break die;
 											}
 											try {
 												rlock();
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												break die;
 											}
 
 											try {
 												ip = nickinfo.exists(P_INPORT);
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												try {
 													runlock();
-												} catch(Exception ee) {
+												} catch (Exception ee) {
 													break die;
 												}
 												break die;
 											}
 											try {
 												runlock();
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												break die;
 											}
 
-											if(ip) {
+											if (ip) {
 												out.println("OK inbound port set");
 											} else {
 												out.println("ERROR port out of range");
 											}
 
 										}
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 
@@ -973,196 +1005,233 @@ die:                            {
 									nns(out);
 								}
 
-							} else if(Command.equals(C_outport)) {
+							} else if (Command.equals(C_outport)) {
 								// Set the NamedDB outbound FROM the router port
 								// BOB --> app
-								if(ns) {
+								if (ns) {
 									try {
-										if(tunnelactive(nickinfo)) {
+										if (tunnelactive(nickinfo)) {
 											out.println("ERROR tunnel is active");
 										} else {
 											int prt;
 											try {
 												wlock();
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												break die;
 											}
 
 											try {
 												nickinfo.kill(P_OUTPORT);
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												try {
 													wunlock();
-												} catch(Exception ee) {
+												} catch (Exception ee) {
 													break die;
 												}
 												break die;
 											}
 											try {
 												prt = Integer.parseInt(Arg);
-												if(prt > 1 && prt < 65536) {
+												if (prt > 1 && prt < 65536) {
 													try {
 														nickinfo.add(P_OUTPORT, new Integer(prt));
-													} catch(Exception ex) {
+													} catch (Exception ex) {
 														try {
 															wunlock();
-														} catch(Exception ee) {
+														} catch (Exception ee) {
 															break die;
 														}
 														break die;
 													}
 												}
 
-											} catch(NumberFormatException nfe) {
+											} catch (NumberFormatException nfe) {
 												out.println("ERROR not a number");
 											}
 
 											try {
 												wunlock();
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												break die;
 											}
 											try {
 												rlock();
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												break die;
 											}
 
 											try {
 												ip = nickinfo.exists(P_OUTPORT);
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												try {
 													runlock();
-												} catch(Exception ee) {
+												} catch (Exception ee) {
 													break die;
 												}
 												break die;
 											}
 											try {
 												runlock();
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												break die;
 											}
 
-											if(ip) {
+											if (ip) {
 												out.println("OK outbound port set");
 											} else {
 												out.println("ERROR port out of range");
 											}
 
 										}
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 
 								} else {
 									try {
 										nns(out);
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 								}
 
-							} else if(Command.equals(C_inhost)) {
-								if(ns) {
+							} else if (Command.equals(C_inhost)) {
+								if (ns) {
 									try {
-										if(tunnelactive(nickinfo)) {
+										if (tunnelactive(nickinfo)) {
 											out.println("ERROR tunnel is active");
 										} else {
 											try {
 												wlock();
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												break die;
 											}
 											try {
 												nickinfo.add(P_INHOST, Arg);
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												try {
 													wunlock();
-												} catch(Exception ee) {
+												} catch (Exception ee) {
 													break die;
 												}
 												break die;
 											}
 											try {
 												wunlock();
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												break die;
 											}
 
 											out.println("OK inhost set");
 										}
 
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 
 								} else {
 									try {
 										nns(out);
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 								}
 
-							} else if(Command.equals(C_outhost)) {
-								if(ns) {
+							} else if (Command.equals(C_outhost)) {
+								if (ns) {
 									try {
-										if(tunnelactive(nickinfo)) {
+										if (tunnelactive(nickinfo)) {
 											out.println("ERROR tunnel is active");
 										} else {
 											try {
 												wlock();
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												break die;
 											}
 											try {
 												nickinfo.add(P_OUTHOST, Arg);
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												try {
 													wunlock();
-												} catch(Exception ee) {
+												} catch (Exception ee) {
 													break die;
 												}
 												break die;
 											}
 											try {
 												wunlock();
-											} catch(Exception ex) {
+											} catch (Exception ex) {
 												break die;
 											}
 
 											out.println("OK outhost set");
 										}
 
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 
 								} else {
 									try {
 										nns(out);
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 								}
 
-							} else if(Command.equals(C_show)) {
+							} else if (Command.equals(C_show)) {
 								// Get the current NamedDB properties
-								if(ns) {
+								if (ns) {
 									out.print("OK");
 									try {
 										rlock();
-									} catch(Exception e) {
+									} catch (Exception e) {
 										break die;
 									}
 
 									try {
 										nickprint(out, nickinfo);
-									} catch(Exception e) {
+									} catch (Exception e) {
+										try {
+											runlock();
+										} catch (Exception ee) {
+											break die;
+										}
+
+										out.println(); // this will cause an IOE if IOE
+										break die;
+									}
+
+									try {
+										runlock();
+									} catch (Exception e) {
+										break die;
+									}
+
+								} else {
+									try {
+										nns(out);
+									} catch (Exception e) {
+										break die;
+									}
+								}
+
+							} else if (Command.equals(C_show_props)) {
+								// Get the current options properties
+								if (ns) {
+									out.print("OK");
+									try {
+										rlock();
+									} catch (Exception e) {
+										break die;
+									}
+
+									try {
+										propprint(out, nickinfo);
+									} catch (Exception e) {
 										try {
 											runlock();
-										} catch(Exception ee) {
+										} catch (Exception ee) {
 											break die;
 										}
 
@@ -1172,23 +1241,23 @@ die:                            {
 
 									try {
 										runlock();
-									} catch(Exception e) {
+									} catch (Exception e) {
 										break die;
 									}
 
 								} else {
 									try {
 										nns(out);
-									} catch(Exception e) {
+									} catch (Exception e) {
 										break die;
 									}
 								}
 
-							} else if(Command.equals(C_start)) {
+							} else if (Command.equals(C_start)) {
 								// Start the tunnel, if we have all the information
-								if(ns && dk && (ip || op)) {
+								if (ns && dk && (ip || op)) {
 									try {
-										if(tunnelactive(nickinfo)) {
+										if (tunnelactive(nickinfo)) {
 											out.println("ERROR tunnel is active");
 										} else {
 											MUXlisten tunnel;
@@ -1197,14 +1266,14 @@ die:                            {
 												Thread t = new Thread(tunnel);
 												t.start();
 												out.println("OK tunnel starting");
-											} catch(I2PException e) {
+											} catch (I2PException e) {
 												out.println("ERROR starting tunnel: " + e);
-											} catch(IOException e) {
+											} catch (IOException e) {
 												out.println("ERROR starting tunnel: " + e);
 											}
 
 										}
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 
@@ -1212,26 +1281,26 @@ die:                            {
 									out.println("ERROR tunnel settings incomplete");
 								}
 
-							} else if(Command.equals(C_stop)) {
+							} else if (Command.equals(C_stop)) {
 								// Stop the tunnel, if it is running
-								if(ns) {
+								if (ns) {
 									try {
 										rlock();
-									} catch(Exception e) {
+									} catch (Exception e) {
 										break die;
 									}
 
 									try {
-										if(nickinfo.get(P_RUNNING).equals(Boolean.TRUE) && nickinfo.get(P_STOPPING).equals(Boolean.FALSE) && nickinfo.get(P_STARTING).equals(Boolean.FALSE)) {
+										if (nickinfo.get(P_RUNNING).equals(Boolean.TRUE) && nickinfo.get(P_STOPPING).equals(Boolean.FALSE) && nickinfo.get(P_STARTING).equals(Boolean.FALSE)) {
 											try {
 												runlock();
-											} catch(Exception e) {
+											} catch (Exception e) {
 												break die;
 											}
 
 											try {
 												wlock();
-											} catch(Exception e) {
+											} catch (Exception e) {
 												break die;
 											}
 
@@ -1239,7 +1308,7 @@ die:                            {
 											try {
 												wunlock();
 
-											} catch(Exception e) {
+											} catch (Exception e) {
 												break die;
 											}
 
@@ -1247,16 +1316,16 @@ die:                            {
 										} else {
 											try {
 												runlock();
-											} catch(Exception e) {
+											} catch (Exception e) {
 												break die;
 											}
 
 											out.println("ERROR tunnel is inactive");
 										}
-									} catch(Exception e) {
+									} catch (Exception e) {
 										try {
 											runlock();
-										} catch(Exception ee) {
+										} catch (Exception ee) {
 											break die;
 										}
 										break die;
@@ -1265,62 +1334,62 @@ die:                            {
 								} else {
 									try {
 										nns(out);
-									} catch(Exception e) {
+									} catch (Exception e) {
 										break die;
 									}
 								}
 
-							} else if(Command.equals(C_clear)) {
+							} else if (Command.equals(C_clear)) {
 								// Clear use of the NamedDB if stopped
-								if(ns) {
+								if (ns) {
 									try {
-										if(tunnelactive(nickinfo)) {
+										if (tunnelactive(nickinfo)) {
 											out.println("ERROR tunnel is active");
 										} else {
 											try {
 												database.getWriteLock();
-											} catch(Exception e) {
+											} catch (Exception e) {
 												break die;
 											}
 											try {
 												database.kill(nickinfo.get(P_NICKNAME));
-											} catch(Exception e) {
+											} catch (Exception e) {
 												try {
 													database.releaseWriteLock();
-												} catch(Exception ee) {
+												} catch (Exception ee) {
 													break die;
 												}
 												break die;
 											}
 											try {
 												database.releaseWriteLock();
-											} catch(Exception e) {
+											} catch (Exception e) {
 												break die;
 											}
 											dk = ns = ip = op = false;
 											out.println("OK cleared");
 										}
 
-									} catch(Exception ex) {
+									} catch (Exception ex) {
 										break die;
 									}
 
 								} else {
 									try {
 										nns(out);
-									} catch(Exception e) {
+									} catch (Exception e) {
 										break die;
 									}
 								}
 
-							} else if(Command.equals(C_status)) {
+							} else if (Command.equals(C_status)) {
 								try {
-									if(database.exists(Arg)) {
+									if (database.exists(Arg)) {
 										// Show status of a NamedDB
 										out.print("OK ");
 										try {
 											ttlpnt(out, Arg);
-										} catch(Exception e) {
+										} catch (Exception e) {
 											out.println(); // this will cause an IOE if IOE
 											break die;
 										}
@@ -1328,11 +1397,11 @@ die:                            {
 									} else {
 										try {
 											nns(out);
-										} catch(Exception e) {
+										} catch (Exception e) {
 											break die;
 										}
 									}
-								} catch(Exception e) {
+								} catch (Exception e) {
 									break die;
 								}
 
@@ -1350,7 +1419,7 @@ die:                            {
 			out.println("OK Bye!");
 
 			server.close();
-		} catch(IOException ioe) {
+		} catch (IOException ioe) {
 			BOB.warn("IOException on socket listen: " + ioe);
 			ioe.printStackTrace();
 		}
diff --git a/apps/BOB/src/net/i2p/BOB/I2Plistener.java b/apps/BOB/src/net/i2p/BOB/I2Plistener.java
index c30e751f5c0456c91eadaecad9fa16ce675eb288..1561b7a222891c3d147453e3d97dc4a3ec9547a8 100644
--- a/apps/BOB/src/net/i2p/BOB/I2Plistener.java
+++ b/apps/BOB/src/net/i2p/BOB/I2Plistener.java
@@ -70,7 +70,7 @@ public class I2Plistener implements Runnable {
 		boolean g = false;
 		I2PSocket sessSocket = null;
 
-		serverSocket.setSoTimeout(1000);
+		serverSocket.setSoTimeout(100);
 		database.getReadLock();
 		info.getReadLock();
 		if(info.exists("INPORT")) {
@@ -107,32 +107,31 @@ public class I2Plistener implements Runnable {
 				//	System.out.println("Exception " + e);
 			}
 		}
-
+		// System.out.println("I2Plistener: Close");
 		try {
 			serverSocket.close();
 		} catch(I2PException e) {
 			// nop
 		}
-
-		while(Thread.activeCount() > tgwatch) { // wait for all threads in our threadgroup to finish
-//			System.out.println("STOP Thread count " + Thread.activeCount());
-			try {
-				Thread.sleep(1000); //sleep for 1000 ms (One second)
-			} catch(Exception e) {
-				// nop
-				}
-		}
-
-		//		System.out.println("STOP Thread count " + Thread.activeCount());
 		// need to kill off the socket manager too.
 		I2PSession session = socketManager.getSession();
 		if(session != null) {
+			// System.out.println("I2Plistener: destroySession");
 			try {
 				session.destroySession();
 			} catch(I2PSessionException ex) {
 				// nop
 			}
-//			System.out.println("destroySession Thread count " + Thread.activeCount());
 		}
+		// System.out.println("I2Plistener: Waiting for children");
+		while(Thread.activeCount() > tgwatch) { // wait for all threads in our threadgroup to finish
+			try {
+				Thread.sleep(100); //sleep for 100 ms (One tenth second)
+			} catch(Exception e) {
+				// nop
+			}
+		}
+
+		// System.out.println("I2Plistener: Done.");
 	}
 }
diff --git a/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java b/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java
index dae9f3e0ff2392fd9b48c51a2dbc990924eaa114..5d24e19d36bcf629d2c24f2dd3156db0d0181900 100644
--- a/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java
+++ b/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java
@@ -119,20 +119,26 @@ die:            {
 						// nop
 					}
 				}
-
+				// System.out.println("I2PtoTCP: Going away...");
 			} catch(Exception e) {
+				// System.out.println("I2PtoTCP: Owch! damn!");
 				break die;
 			}
 		} // die
 		try {
+			// System.out.println("I2PtoTCP: Close I2P");
 			I2P.close();
 		} catch(Exception e) {
 			tell = false;
 		}
+		//System.out.println("I2PtoTCP: Closed I2P");
 		try {
+			// System.out.println("I2PtoTCP: Close sock");
 			sock.close();
 		} catch(Exception e) {
 			tell = false;
 		}
+		// System.out.println("I2PtoTCP: Done");
+
 	}
 }
diff --git a/apps/BOB/src/net/i2p/BOB/Lifted.java b/apps/BOB/src/net/i2p/BOB/Lifted.java
new file mode 100644
index 0000000000000000000000000000000000000000..fbd23cba5feb7dedf9b5cb25d57335baa0dbfe57
--- /dev/null
+++ b/apps/BOB/src/net/i2p/BOB/Lifted.java
@@ -0,0 +1,56 @@
+/**
+ *            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ *                    Version 2, December 2004
+ *
+ * Copyright (C) sponge
+ *   Planet Earth
+ * Everyone is permitted to copy and distribute verbatim or modified
+ * copies of this license document, and changing it is allowed as long
+ * as the name is changed.
+ *
+ *            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ *   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+ *
+ *  0. You just DO WHAT THE FUCK YOU WANT TO.
+ *
+ * See...
+ *
+ *	http://sam.zoy.org/wtfpl/
+ *	and
+ *	http://en.wikipedia.org/wiki/WTFPL
+ *
+ * ...for any additional details and liscense questions.
+ */
+package net.i2p.BOB;
+
+import java.util.Enumeration;
+import java.util.Properties;
+
+/**
+ * Sets of "friendly" utilities to make life easier.
+ * Any "Lifted" code will apear here, and credits given.
+ * It's better to "Lift" a small chunk of "free" code than add in piles of
+ * code we don't need, and don't want.
+ *
+ * @author sponge
+ */
+public class Lifted {
+
+	/**
+	 * Copy a set of properties from one Property to another.
+	 * Lifted from Apache Derby code svn repository.
+	 * Liscenced as follows:
+	 * http://svn.apache.org/repos/asf/db/derby/code/trunk/LICENSE
+	 *
+	 * @param src_prop  Source set of properties to copy from.
+	 * @param dest_prop Dest Properties to copy into.
+	 *
+	 **/
+	public static void copyProperties(Properties src_prop, Properties dest_prop) {
+		for (Enumeration propertyNames = src_prop.propertyNames();
+			propertyNames.hasMoreElements();) {
+			Object key = propertyNames.nextElement();
+			dest_prop.put(key, src_prop.get(key));
+		}
+	}
+}
diff --git a/apps/BOB/src/net/i2p/BOB/MUXlisten.java b/apps/BOB/src/net/i2p/BOB/MUXlisten.java
index a16c830b1c0f011dcf2b2cef58a3e1b6a346650c..bd52e27fd2deac44d2a0f06b1e20aaa5be4f832f 100644
--- a/apps/BOB/src/net/i2p/BOB/MUXlisten.java
+++ b/apps/BOB/src/net/i2p/BOB/MUXlisten.java
@@ -32,6 +32,7 @@ import net.i2p.I2PException;
 import net.i2p.client.streaming.I2PSocketManager;
 import net.i2p.client.streaming.I2PSocketManagerFactory;
 import net.i2p.util.Log;
+import org.tanukisoftware.wrapper.WrapperManager;
 
 /**
  *
@@ -64,6 +65,7 @@ public class MUXlisten implements Runnable {
 	MUXlisten(NamedDB database, NamedDB info, Log _log) throws I2PException, IOException, RuntimeException {
 		int port = 0;
 		InetAddress host = null;
+		this.tg = null;
 		this.database = database;
 		this.info = info;
 		this._log = _log;
@@ -72,7 +74,10 @@ public class MUXlisten implements Runnable {
 		this.info.getReadLock();
 		N = this.info.get("NICKNAME").toString();
 		prikey = new ByteArrayInputStream((byte[])info.get("KEYS"));
-		Properties Q = (Properties)info.get("PROPERTIES");
+		// Make a new copy so that anything else won't muck with our database.
+		Properties R = (Properties)info.get("PROPERTIES");
+		Properties Q = new Properties();
+		Lifted.copyProperties(R, Q);
 		this.database.releaseReadLock();
 		this.info.releaseReadLock();
 
@@ -207,23 +212,39 @@ die:                            {
 						break die;
 					}
 				} // die
+
 				// wait for child threads and thread groups to die
+				// System.out.println("MUXlisten: waiting for children");
 				while(tg.activeCount() + tg.activeGroupCount() != 0) {
+					tg.interrupt(); // unwedge any blocking threads.
 					try {
-						Thread.sleep(1000); //sleep for 1000 ms (One second)
+						Thread.sleep(100); //sleep for 100 ms (One tenth second)
 					} catch(InterruptedException ex) {
 						// nop
-						}
+					}
 				}
 				tg.destroy();
 				// Zap reference to the ThreadGroup so the JVM can GC it.
 				tg = null;
 			} catch(Exception e) {
+				// System.out.println("MUXlisten: Caught an exception" + e);
 				break quit;
 			}
 		} // quit
-		socketManager.destroySocketManager();
-		// zero out everything, just incase.
+		// This is here to catch when something fucks up REALLY bad.
+		if(tg != null) {
+			System.out.println("BOB: MUXlisten: Something fucked up REALLY bad!");
+			System.out.println("BOB: MUXlisten: Please email the following dump to sponge@mail.i2p");
+			WrapperManager.requestThreadDump();
+			System.out.println("BOB: MUXlisten: Something fucked up REALLY bad!");
+			System.out.println("BOB: MUXlisten: Please email the above dump to sponge@mail.i2p");
+		}
+			// zero out everything, just incase.
+		try {
+			socketManager.destroySocketManager();
+		} catch(Exception e) {
+			// nop
+		}
 		try {
 			wlock();
 			try {
@@ -236,7 +257,20 @@ die:                            {
 			}
 			wunlock();
 		} catch(Exception e) {
-			return;
+		}
+		// This is here to catch when something fucks up REALLY bad.
+		if(tg != null) {
+			while(tg.activeCount() + tg.activeGroupCount() != 0) {
+					tg.interrupt(); // unwedge any blocking threads.
+					try {
+						Thread.sleep(100); //sleep for 100 ms (One tenth second)
+					} catch(InterruptedException ex) {
+						// nop
+					}
+			}
+			tg.destroy();
+			// Zap reference to the ThreadGroup so the JVM can GC it.
+			tg = null;
 		}
 	}
 }
diff --git a/apps/BOB/src/net/i2p/BOB/TCPio.java b/apps/BOB/src/net/i2p/BOB/TCPio.java
index e9024105afa93b67440ed73f19855d470a22408a..25290bcdcf1d15630848a5c414a11989482f229d 100644
--- a/apps/BOB/src/net/i2p/BOB/TCPio.java
+++ b/apps/BOB/src/net/i2p/BOB/TCPio.java
@@ -23,7 +23,6 @@
  */
 package net.i2p.BOB;
 
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
@@ -91,12 +90,16 @@ public class TCPio implements Runnable {
 					 * the stream has been reached.
 					 *
 					 */
+					// System.out.println("TCPio: End Of Stream");
 					return;
 				}
 			}
+			// System.out.println("TCPio: RUNNING = false");
 		} catch(Exception e) {
 			// Eject!!! Eject!!!
+			// System.out.println("TCPio: Caught an exception " + e);
 			return;
 		}
+		// System.out.println("TCPio: Leaving.");
 	}
 }
diff --git a/apps/BOB/src/net/i2p/BOB/TCPlistener.java b/apps/BOB/src/net/i2p/BOB/TCPlistener.java
index 72b34eac86d014688b1edac109805f8f691ec946..99ae047d31cb10f7d698d28a59e0ae09ed65fe06 100644
--- a/apps/BOB/src/net/i2p/BOB/TCPlistener.java
+++ b/apps/BOB/src/net/i2p/BOB/TCPlistener.java
@@ -76,7 +76,6 @@ public class TCPlistener implements Runnable {
 			tgwatch = 2;
 		}
 		try {
-//			System.out.println("Starting thread count " + Thread.activeCount());
 			Socket server = new Socket();
 			listener.setSoTimeout(1000);
 			info.releaseReadLock();
@@ -87,7 +86,6 @@ public class TCPlistener implements Runnable {
 				spin = info.get("RUNNING").equals(Boolean.TRUE);
 				info.releaseReadLock();
 				database.releaseReadLock();
-//				System.out.println("Thread count " + Thread.activeCount());
 				try {
 					server = listener.accept();
 					g = true;
@@ -102,8 +100,13 @@ public class TCPlistener implements Runnable {
 					g = false;
 				}
 			}
+			//System.out.println("TCPlistener: destroySession");
 			listener.close();
 		} catch(IOException ioe) {
+			try {
+				listener.close();
+			} catch(IOException e) {
+			}
 			// Fatal failure, cause a stop event
 			database.getReadLock();
 			info.getReadLock();
@@ -120,17 +123,6 @@ public class TCPlistener implements Runnable {
 			}
 		}
 
-//System.out.println("STOP!");
-
-		while(Thread.activeCount() > tgwatch) { // wait for all threads in our threadgroup to finish
-//			System.out.println("STOP Thread count " + Thread.activeCount());
-			try {
-				Thread.sleep(1000); //sleep for 1000 ms (One second)
-			} catch(Exception e) {
-				// nop
-				}
-		}
-//		System.out.println("STOP Thread count " + Thread.activeCount());
 		// need to kill off the socket manager too.
 		I2PSession session = socketManager.getSession();
 		if(session != null) {
@@ -139,8 +131,16 @@ public class TCPlistener implements Runnable {
 			} catch(I2PSessionException ex) {
 				// nop
 			}
-//			System.out.println("destroySession Thread count " + Thread.activeCount());
 		}
+		//System.out.println("TCPlistener: Waiting for children");
+		while(Thread.activeCount() > tgwatch) { // wait for all threads in our threadgroup to finish
+			try {
+				Thread.sleep(100); //sleep for 100 ms (One tenth second)
+			} catch(Exception e) {
+				// nop
+				}
+		}
+		//System.out.println("TCPlistener: Done.");
 	}
 }
 
diff --git a/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java b/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java
index 3323d0450b1cd5491f74d71a4cce5fd812a86af3..df61e78e1b1fc7700d6cc7cd7c5b16d2ef4e446b 100644
--- a/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java
+++ b/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java
@@ -152,6 +152,7 @@ public class TCPtoI2P implements Runnable {
 							// nop
 						}
 					}
+					// System.out.println("TCPtoI2P: Going away...");
 
 				} catch(I2PException e) {
 					Emsg("ERROR " + e.toString(), out);
@@ -169,14 +170,17 @@ public class TCPtoI2P implements Runnable {
 		} catch(IOException ioe) {
 		}
 		try {
+			// System.out.println("TCPtoI2P: Close I2P");
 			I2P.close();
 		} catch(Exception e) {
 		}
 
 		try {
+			// System.out.println("TCPtoI2P: Close sock");
 			sock.close();
 		} catch(Exception e) {
 		}
+		// System.out.println("TCPtoI2P: Done.");
+
 	}
 }
-
diff --git a/apps/bogobot/Bogobot.java b/apps/bogobot/Bogobot.java
deleted file mode 100644
index 0c0c40c92e97d7134eb336182ebfc2e254c7ef16..0000000000000000000000000000000000000000
--- a/apps/bogobot/Bogobot.java
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * bogobot - A simple join/part stats logger bot for I2P IRC.
- * 
- * Bogobot.java
- * 2004 The I2P Project
- * http://www.i2p.net
- * This code is public domain.
- */
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.Properties;
-import java.util.Timer;
-import java.util.TimerTask;
-
-import org.apache.log4j.DailyRollingFileAppender;
-import org.apache.log4j.Level;
-import org.apache.log4j.Logger;
-import org.apache.log4j.PatternLayout;
-import org.jibble.pircbot.IrcException;
-import org.jibble.pircbot.NickAlreadyInUseException;
-import org.jibble.pircbot.PircBot;
-import org.jibble.pircbot.User;
-
-/**
- * TODO 0.5 Add multi-server capability.
- * 
- * @author hypercubus, oOo
- * @version 0.4
- */
-public class Bogobot extends PircBot {
-
-    private static final String INTERVAL_DAILY   = "daily";
-    private static final String INTERVAL_MONTHLY = "monthly";
-    private static final String INTERVAL_WEEKLY  = "weekly";
-
-    private boolean _isIntentionalDisconnect      = false;
-    private long    _lastUserlistCommandTimestamp = 0;
-    private Logger  _logger                       = Logger.getLogger(Bogobot.class);
-
-    private int     _currentAutoRoundTripTag      = 0;
-    private long    _lastAutoRoundTripSentTime    = 0;
-    private Timer   _tickTimer;
-
-    private String  _configFile;
-
-    private String  _botPrimaryNick;
-    private String  _botSecondaryNick;
-    private String  _botNickservPassword;
-    private String  _botUsername;
-    private String  _ownerPrimaryNick;
-    private String  _ownerSecondaryNick;
-    private String  _botShutdownPassword;
-    private String  _ircChannel;
-    private String  _ircServer;
-    private int     _ircServerPort;
-    private boolean _isLoggerEnabled;
-    private String  _loggedHostnamePattern;
-    private boolean _isUserlistCommandEnabled;
-    private String  _logFilePrefix;
-    private String  _logFileRotationInterval;
-    private long    _commandAntiFloodInterval;
-    private String  _userlistCommandTrigger;
-    private boolean _isRoundTripDelayEnabled;
-    private int     _roundTripDelayPeriod;
-
-    class BogobotTickTask extends TimerTask {
-        private Bogobot _caller;
-
-        public BogobotTickTask(Bogobot caller) {
-            _caller = caller;
-        }
-
-        public void run() {
-          _caller.onTick();
-        }
-    }
-
-    private void loadConfigFile(String configFileName) {
-
-        _configFile = configFileName;
-
-        Properties config = new Properties();
-        FileInputStream fis = null;
-
-        try {
-            fis = new FileInputStream(configFileName);
-            config.load(fis);
-        } catch (IOException ioe) {
-            System.err.println("Error loading configuration file");
-            System.exit(2);
-
-        } finally {
-            if (fis != null) try {
-                fis.close();
-            } catch (IOException ioe) { // nop
-            }
-        }
-
-        _botPrimaryNick = config.getProperty("botPrimaryNick", "somebot");
-        _botSecondaryNick = config.getProperty("botSecondaryNick", "somebot_");
-        _botNickservPassword = config.getProperty("botNickservPassword", "");
-        _botUsername = config.getProperty("botUsername", "somebot");
-
-        _ownerPrimaryNick = config.getProperty("ownerPrimaryNick", "somenick");
-        _ownerSecondaryNick = config.getProperty("ownerSecondaryNick", "somenick_");
-
-        _botShutdownPassword = config.getProperty("botShutdownPassword", "take off eh");
-
-        _ircChannel = config.getProperty("ircChannel", "#i2p-chat");
-        _ircServer = config.getProperty("ircServer", "irc.postman.i2p");
-        _ircServerPort = Integer.parseInt(config.getProperty("ircServerPort", "6668"));
-
-        _isLoggerEnabled = Boolean.valueOf(config.getProperty("isLoggerEnabled", "true")).booleanValue();
-        _loggedHostnamePattern = config.getProperty("loggedHostnamePattern", "");
-        _logFilePrefix = config.getProperty("logFilePrefix", "irc.postman.i2p.i2p-chat");
-        _logFileRotationInterval = config.getProperty("logFileRotationInterval", INTERVAL_DAILY);
-
-        _isRoundTripDelayEnabled = Boolean.valueOf(config.getProperty("isRoundTripDelayEnabled", "false")).booleanValue();
-        _roundTripDelayPeriod = Integer.parseInt(config.getProperty("roundTripDelayPeriod", "300"));
-
-        _isUserlistCommandEnabled = Boolean.valueOf(config.getProperty("isUserlistCommandEnabled", "true")).booleanValue();
-        _userlistCommandTrigger = config.getProperty("userlistCommandTrigger", "!who");
-        _commandAntiFloodInterval = Long.parseLong(config.getProperty("commandAntiFloodInterval", "60"));
-    }
-
-    public Bogobot(String configFileName) {
-
-        loadConfigFile(configFileName);
-
-        this.setName(_botPrimaryNick);
-        this.setLogin(_botUsername);
-        _tickTimer = new Timer();
-        _tickTimer.scheduleAtFixedRate(new BogobotTickTask(this), 1000, 10 * 1000);
-    }
-
-    public static void main(String[] args) {
-
-        Bogobot bogobot;
-
-        if (args.length > 1) {
-            System.err.println("Too many arguments, the only allowed parameter is configuration file name");
-            System.exit(3);
-        }
-        if (args.length == 1) {
-          bogobot = new Bogobot(args[0]);
-        } else {
-          bogobot = new Bogobot("bogobot.config");
-        }
-
-        bogobot.setVerbose(true);
-
-        if (bogobot._isLoggerEnabled)
-            bogobot.initLogger();
-
-        bogobot.connectToServer();
-    }
-
-    protected void onTick() {
-        // Tick about once every ten seconds
-
-        if (this.isConnected() && _isRoundTripDelayEnabled) {
-            if( ( (System.currentTimeMillis() - _lastAutoRoundTripSentTime) >= (_roundTripDelayPeriod * 1000) ) && (this.getOutgoingQueueSize() == 0) ) {
-                // Connected, sending queue is empty and last RoundTrip is more then 5 minutes old -> Send a new one
-                _currentAutoRoundTripTag ++;
-                _lastAutoRoundTripSentTime = System.currentTimeMillis();
-                sendNotice(this.getNick(),"ROUNDTRIP " + _currentAutoRoundTripTag);
-            }
-        }
-    }
-
-    protected void onDisconnect() {
-
-        if (_isIntentionalDisconnect)
-            System.exit(0);            
-
-        if (_isLoggerEnabled)
-            _logger.info(System.currentTimeMillis() + " quits *** " + this.getName() + " *** (Lost connection)");
-
-        try {
-            Thread.sleep(60000);
-        } catch (InterruptedException e) {
-            // No worries.
-        }
-        connectToServer();
-    }
-
-    protected void onJoin(String channel, String sender, String login, String hostname) {
-
-        if (_isLoggerEnabled) {
-            if (sender.equals(this.getName())) {
-
-                _logger.info(System.currentTimeMillis() + " joins *** " + _botPrimaryNick + " ***");
-
-            } else {
-
-                String prependedHostname = "@" + hostname;
-                if (prependedHostname.endsWith(_loggedHostnamePattern)) {
-                    _logger.info(System.currentTimeMillis() + " joins " + sender);
-                }
-
-            }
-        }
-    }
-
-    protected void onMessage(String channel, String sender, String login, String hostname, String message) {
-        message = message.replaceFirst("<.+?> ", "");
-        if (_isUserlistCommandEnabled && message.equals(_userlistCommandTrigger)) {
-
-            if (System.currentTimeMillis() - _lastUserlistCommandTimestamp < _commandAntiFloodInterval * 1000)
-                return;
-
-            Object[] users = getUsers(_ircChannel);
-            String output = "Userlist for " + _ircChannel + ": ";
-
-            for (int i = 0; i < users.length; i++)
-                output += "[" + ((User) users[i]).getNick() + "] ";
-
-            sendMessage(_ircChannel, output);
-            _lastUserlistCommandTimestamp = System.currentTimeMillis();
-        }
-    }
-
-    protected void onPart(String channel, String sender, String login, String hostname) {
-
-        if (_isLoggerEnabled) {
-            if (sender.equals(this.getName())) {
-                _logger.info(System.currentTimeMillis() + " parts *** " + _botPrimaryNick + " ***");
-            } else {
-                String prependedHostname = "@" + hostname;
-                if (prependedHostname.endsWith(_loggedHostnamePattern)) {
-                    _logger.info(System.currentTimeMillis() + " parts " + sender);
-                }
-            }
-        }
-
-    }
-
-    protected void onPrivateMessage(String sender, String login, String hostname, String message) {
-        /*
-         * Nobody else except the bot's owner can shut it down, unless of
-         * course the owner's nick isn't registered and someone's spoofing it.
-         */
-        if ((sender.equals(_ownerPrimaryNick) || sender.equals(_ownerSecondaryNick)) && message.equals(_botShutdownPassword)) {
-
-            if (_isLoggerEnabled)
-                _logger.info(System.currentTimeMillis() + " quits *** " + this.getName() + " ***");
-
-            _isIntentionalDisconnect = true;
-            disconnect();
-        }
-    }
-
-    protected void onQuit(String sourceNick, String sourceLogin, String sourceHostname, String reason) {
-        String prependedHostname = "@" + sourceHostname;
-
-        if (sourceNick.equals(_botPrimaryNick))
-            changeNick(_botPrimaryNick);
-
-        if (_isLoggerEnabled) {
-            if (prependedHostname.endsWith(_loggedHostnamePattern)) {
-                _logger.info(System.currentTimeMillis() + " quits " + sourceNick + " " + reason);
-            }
-        }
-
-    }
-
-    private void connectToServer() {
-
-        int loginAttempts = 0;
-
-        while (true) {
-            try {
-                connect(_ircServer, _ircServerPort);
-                break;
-            } catch (NickAlreadyInUseException e) {
-                if (loginAttempts == 1) {
-                    System.out.println("Sorry, the primary and secondary bot nicks are already taken. Exiting.");
-                    System.exit(1);
-                }
-                loginAttempts++;
-                try {
-                    Thread.sleep(5000);
-                } catch (InterruptedException e1) {
-                    // Hmph.
-                }
-
-                if (getName().equals(_botPrimaryNick))
-                    setName(_botSecondaryNick);
-                else
-                    setName(_botPrimaryNick);
-
-                continue;
-            } catch (IOException e) {
-                System.out.println("Error during login: ");
-                e.printStackTrace();
-                System.exit(1);
-            } catch (IrcException e) {
-                System.out.println("Error during login: ");
-                e.printStackTrace();
-                System.exit(1);
-            }
-        }
-        joinChannel(_ircChannel);
-    }
-
-    protected void onNotice(String sourceNick, String sourceLogin, String sourceHostname, String target, String notice) {
-
-        if (sourceNick.equals("NickServ") && (notice.indexOf("/msg NickServ IDENTIFY") >= 0) && (_botNickservPassword != "")) {
-            sendRawLineViaQueue("NICKSERV IDENTIFY " + _botNickservPassword);
-        }
-
-        if (sourceNick.equals(getNick()) && notice.equals( "ROUNDTRIP " + _currentAutoRoundTripTag)) {
-            int delay = (int)((System.currentTimeMillis() - _lastAutoRoundTripSentTime) / 100);
-//            sendMessage(_ircChannel, "Round-trip delay = " + (delay / 10.0f) + " seconds");
-            if (_isLoggerEnabled)
-                _logger.info(System.currentTimeMillis() + " roundtrip " + delay);
-        }
-    }
-
-    private void initLogger() {
-
-        String                   logFilePath         = "logs" + File.separator + _logFilePrefix;
-        DailyRollingFileAppender rollingFileAppender = null;
-
-        if (!(new File("logs").exists()))
-            (new File("logs")).mkdirs();
-
-        try {
-
-            if (_logFileRotationInterval.equals("monthly"))
-                rollingFileAppender = new DailyRollingFileAppender(new PatternLayout("%m%n"), logFilePath, "'.'yyyy-MM'.log'");
-            else if (_logFileRotationInterval.equals("weekly"))
-                rollingFileAppender = new DailyRollingFileAppender(new PatternLayout("%m%n"), logFilePath, "'.'yyyy-ww'.log'");
-            else
-                rollingFileAppender = new DailyRollingFileAppender(new PatternLayout("%m%n"), logFilePath, "'.'yyyy-MM-dd'.log'");
-
-            rollingFileAppender.setThreshold(Level.INFO);
-            _logger.addAppender(rollingFileAppender);
-        } catch (IOException ex) {
-            System.out.println("Error: Couldn't create or open an existing log file. Exiting.");
-            System.exit(1);
-        }
-    }
-
-}
diff --git a/apps/bogobot/Bogoparser.java b/apps/bogobot/Bogoparser.java
deleted file mode 100644
index 9b19443967a5ee87dcd9a7f126b575ccfc7bce9b..0000000000000000000000000000000000000000
--- a/apps/bogobot/Bogoparser.java
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * bogoparser - A simple logfile analyzer for bogobot.
- * 
- * Bogoparser.java
- * 2004 The I2P Project
- * http://www.i2p.net
- * This code is public domain.
- */
-
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * @author hypercubus
- * @version 0.4
- */
-public class Bogoparser {
-
-    private static void displayUsageAndExit() {
-        System.out.println("\r\nUsage:\r\n\r\n    java Bogoparser [--by-duration] <logfile>\r\n");
-        System.exit(1);
-    }
-
-    public static void main(String[] args) {
-
-        Bogoparser bogoparser;
-
-        if (args.length < 1 || args.length > 2)
-            displayUsageAndExit();
-
-        if (args.length == 2) {
-            if (!args[0].equals("--by-duration"))
-                displayUsageAndExit();
-            bogoparser = new Bogoparser(args[1], true);
-        }
-
-        if (args.length == 1)
-            bogoparser = new Bogoparser(args[0], false);
-    }
-
-    private Bogoparser(String logfile, boolean sortByDuration) {
-
-        ArrayList sortedSessions;
-
-        if (sortByDuration) {
-            sortedSessions = sortSessionsByDuration(calculateSessionDurations(sortSessionsByTime(readLogfile(logfile))));
-            formatAndOutputByDuration(sortedSessions);
-        } else {
-            sortedSessions = calculateSessionDurations(sortSessionsByQuitReason(sortSessionsByNick(sortSessionsByTime(readLogfile(logfile)))));
-            formatAndOutput(sortedSessions);
-        }
-    }
-
-    private ArrayList calculateSessionDurations(ArrayList sortedSessionsByQuitReasonOrDuration) {
-
-        ArrayList calculatedSessionDurations = new ArrayList();
-
-        for (int i = 0; i+1 < sortedSessionsByQuitReasonOrDuration.size(); i += 2) {
-
-            String   joinsEntry       = (String) sortedSessionsByQuitReasonOrDuration.get(i);
-            String[] joinsEntryFields = joinsEntry.split(" ");
-
-            String  quitsEntry = (String) sortedSessionsByQuitReasonOrDuration.get(i+1);
-            Pattern p          = Pattern.compile("^([^ ]+) [^ ]+ ([^ ]+) (.*)$");
-            Matcher m          = p.matcher(quitsEntry);
-
-            if (m.matches()) {
-
-                String currentJoinTime             = joinsEntryFields[0];
-                String currentNick                 = m.group(2);
-                String currentQuitReason           = m.group(3);
-                String currentQuitTime             = m.group(1);
-                long   joinsTimeInMilliseconds;
-                long   quitsTimeInMilliseconds;
-                long   sessionLengthInMilliseconds;
-                
-                joinsTimeInMilliseconds = Long.parseLong(currentJoinTime);
-                quitsTimeInMilliseconds = Long.parseLong(currentQuitTime);
-                sessionLengthInMilliseconds = quitsTimeInMilliseconds - joinsTimeInMilliseconds;
-                
-                String hours   = "" + sessionLengthInMilliseconds/1000/60/60;
-                String minutes = "" + (sessionLengthInMilliseconds/1000/60)%60;
-
-                if (hours.length() < 2)
-                    hours = "0" + hours;
-
-                if (hours.length() < 3)
-                    hours = "0" + hours;
-
-                if (minutes.length() < 2)
-                    minutes = "0" + minutes;
-
-                int    columnPadding       = 19-currentNick.length();
-                String columnPaddingString = " ";
-
-                for (int j = 0; j < columnPadding; j++)
-                    columnPaddingString = columnPaddingString + " ";
-
-                calculatedSessionDurations.add(sessionLengthInMilliseconds + " " + currentNick + columnPaddingString + " online " + hours + " hours " + minutes + " minutes " + currentQuitReason);
-            } else {
-                System.out.println("\r\nError: Unexpected entry in logfile: " + quitsEntry);
-                System.exit(1);
-            }
-        }
-        return calculatedSessionDurations;
-    }
-
-    private void formatAndOutput(ArrayList sortedSessions) {
-
-        String quitReason = null;
-
-        for (int i = 0; i < sortedSessions.size(); i++) {
-
-            String  entry = (String) sortedSessions.get(i);
-            Pattern p     = Pattern.compile("^[\\d]+ ([^ ]+ +online [\\d]+ hours [\\d]+ minutes) (.*)$");
-            Matcher m     = p.matcher(entry);
-
-            if (m.matches()) {
-
-                if (quitReason == null) {
-                    quitReason = m.group(2);
-                    System.out.println("\r\nQUIT: " + ((m.group(2).equals("")) ? "No Reason Given" : quitReason) + "\r\n");
-                }
-
-                String tempQuitReason = m.group(2);
-                String tempSession    = m.group(1);
-
-                if (tempQuitReason.equals(quitReason)) {
-                    System.out.println("    " + tempSession);
-                } else {
-                    quitReason = null;
-                    i -= 1;
-                    continue;
-                }
-            } else {
-                System.out.println("\r\nError: Unexpected entry in logfile: " + entry);
-                System.exit(1);
-            }
-        }
-        System.out.println("\r\n");
-    }
-
-    private void formatAndOutputByDuration(ArrayList sortedSessions) {
-        System.out.println("\r\n");
-
-        for (int i = 0; i < sortedSessions.size(); i++) {
-            String[] columns = ((String) sortedSessions.get(i)).split(" ", 2);
-            System.out.println(columns[1]);
-        }
-
-        System.out.println("\r\n");
-    }
-
-    private ArrayList readLogfile(String logfile) {
-
-        ArrayList log = new ArrayList();
-
-        try {
-            BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(logfile)));
-
-            for (String line; (line = in.readLine()) != null; )
-                log.add(line);
-
-            in.close();
-
-        } catch (FileNotFoundException e) {
-            System.out.println("\r\nError: Can't find logfile '" + logfile + "'.\r\n");
-            System.exit(1);
-
-        } catch (IOException e) {
-            System.out.println("\r\nError: Can't read logfile '" + logfile + "'.\r\n");
-            System.exit(1);
-        }
-        return log;
-    }
-
-    /*
-     * Performs an odd-even transposition sort.
-     */
-    private ArrayList sortSessionsByDuration(ArrayList calculatedSessionDurations) {
-
-        for (int i = 0; i < calculatedSessionDurations.size()/2; i++) {
-            for (int j = 0; j+1 < calculatedSessionDurations.size(); j += 2) {
-
-                String[] currentDurationString = ((String) calculatedSessionDurations.get(j)).split(" ", 2);
-                long     currentDuration       = Long.parseLong(currentDurationString[0]);
-                String[] nextDurationString    = ((String) calculatedSessionDurations.get(j+1)).split(" ", 2);
-                long     nextDuration          = Long.parseLong(nextDurationString[0]);
-
-                if (currentDuration > nextDuration) {
-                    calculatedSessionDurations.add(j, calculatedSessionDurations.get(j+1));
-                    calculatedSessionDurations.remove(j+2);
-                }
-            }
-
-            for (int j = 1; j+1 < calculatedSessionDurations.size(); j += 2) {
-
-                String[] currentDurationString = ((String) calculatedSessionDurations.get(j)).split(" ", 2);
-                long     currentDuration       = Long.parseLong(currentDurationString[0]);
-                String[] nextDurationString    = ((String) calculatedSessionDurations.get(j+1)).split(" ", 2);
-                long     nextDuration          = Long.parseLong(nextDurationString[0]);
-
-                if (currentDuration > nextDuration) {
-                    calculatedSessionDurations.add(j, calculatedSessionDurations.get(j+1));
-                    calculatedSessionDurations.remove(j+2);
-                }
-            }
-        }
-        return calculatedSessionDurations;
-    }
-
-    private ArrayList sortSessionsByNick(ArrayList sortedSessionsByTime) {
-
-        ArrayList sortedSessionsByNick = new ArrayList();
-
-        while (sortedSessionsByTime.size() != 0) {
-
-            String   entry       = (String) sortedSessionsByTime.get(0);
-            String[] entryFields = entry.split(" ");
-            String   currentNick = entryFields[2];
-
-            sortedSessionsByNick.add(entry);
-            sortedSessionsByNick.add(sortedSessionsByTime.get(1));
-            sortedSessionsByTime.remove(0);
-            sortedSessionsByTime.remove(0);
-            for (int i = 0; i+1 < sortedSessionsByTime.size(); i += 2) {
-
-                String   nextEntry       = (String) sortedSessionsByTime.get(i);
-                String[] nextEntryFields = nextEntry.split(" ");
-
-                if (nextEntryFields[2].equals(currentNick)) {
-                    sortedSessionsByNick.add(nextEntry);
-                    sortedSessionsByNick.add(sortedSessionsByTime.get(i+1));
-                    sortedSessionsByTime.remove(i);
-                    sortedSessionsByTime.remove(i);
-                    i -= 2;
-                }
-            }
-        }
-        return sortedSessionsByNick;
-    }
-
-    private ArrayList sortSessionsByQuitReason(ArrayList sortedSessionsByNick) {
-
-        ArrayList sortedSessionsByQuitReason = new ArrayList();
-
-        while (sortedSessionsByNick.size() != 0) {
-
-            String  entry = (String) sortedSessionsByNick.get(1);
-            Pattern p     = Pattern.compile("^[^ ]+ [^ ]+ [^ ]+ (.*)$");
-            Matcher m     = p.matcher(entry);
-
-            if (m.matches()) {
-
-                String currentQuitReason = m.group(1);
-
-                sortedSessionsByQuitReason.add(sortedSessionsByNick.get(0));
-                sortedSessionsByQuitReason.add(entry);
-                sortedSessionsByNick.remove(0);
-                sortedSessionsByNick.remove(0);
-                for (int i = 0; i+1 < sortedSessionsByNick.size(); i += 2) {
-
-                    String  nextEntry = (String) sortedSessionsByNick.get(i+1);
-                    Pattern p2        = Pattern.compile("^[^ ]+ [^ ]+ [^ ]+ (.*)$");
-                    Matcher m2        = p2.matcher(nextEntry);
-
-                    if (m2.matches()) {
-
-                        String nextQuitReason = m2.group(1);
- 
-                        if (nextQuitReason.equals(currentQuitReason)) {
-                            sortedSessionsByQuitReason.add(sortedSessionsByNick.get(i));
-                            sortedSessionsByQuitReason.add(nextEntry);
-                            sortedSessionsByNick.remove(i);
-                            sortedSessionsByNick.remove(i);
-                            i -= 2;
-                        }
-                    } else {
-                        System.out.println("\r\nError: Unexpected entry in logfile: " + nextEntry);
-                        System.exit(1);
-                    }
-                }
-            } else {
-                System.out.println("\r\nError: Unexpected entry in logfile: " + entry);
-                System.exit(1);
-            }
-        }
-        return sortedSessionsByQuitReason;
-    }
-
-    /**
-     * Sessions terminated with "parts" messages instead of "quits" are filtered
-     * out.
-     */
-    private ArrayList sortSessionsByTime(ArrayList log) {
-
-        ArrayList sortedSessionsByTime = new ArrayList();
-
-        mainLoop:
-            while (log.size() > 0) {
-                
-                String   entry       = (String) log.get(0);
-                String[] entryFields = entry.split(" ");
-                
-                if (entryFields[1].equals("quits") && !entryFields[1].equals("joins")) {
-                    /*
-                     * Discard entry. The specified log either doesn't contain
-                     * the corresponding "joins" time for this quit entry or the
-                     * entry is a "parts" or unknown message, and in both cases
-                     * the entry's data is useless.
-                     */
-                    log.remove(0);
-                    continue;
-                }
-                
-                for (int i = 1; i < log.size(); i++) {  // Find corresponding "quits" entry.
-                    
-                    String   tempEntry       = (String) log.get(i);
-                    String[] tempEntryFields = tempEntry.split(" ");
-                    
-                    if (tempEntryFields[2].equals(entryFields[2])) {  // Check if the nick fields for the two entries match.
-                        if (!tempEntryFields[1].equals("quits")) {
-                            if (tempEntryFields[1].equals("joins")) {  // Don't discard a subsequent "joins" entry.
-                                log.remove(0);
-                                continue mainLoop;
-                            }
-                            log.remove(i);
-                            continue;
-                        }
-                        sortedSessionsByTime.add(entry);
-                        sortedSessionsByTime.add(tempEntry);
-                        log.remove(i);
-                        break;
-                    }
-                }
-                /*
-                 * Discard "joins" entry. The specified log doesn't contain the
-                 * corresponding "quits" time for this entry so the entry's
-                 * data is useless.
-                 */
-                
-                log.remove(0);
-            }
-
-        return sortedSessionsByTime;
-    }
-}
diff --git a/apps/bogobot/LICENSE.log4j.txt b/apps/bogobot/LICENSE.log4j.txt
deleted file mode 100644
index 030564fc13327d7344c1867f57ea747cff7eb915..0000000000000000000000000000000000000000
--- a/apps/bogobot/LICENSE.log4j.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * ============================================================================
- *                   The Apache Software License, Version 1.1
- * ============================================================================
- * 
- *    Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without modifica-
- * tion, are permitted provided that the following conditions are met:
- * 
- * 1. Redistributions of  source code must  retain the above copyright  notice,
- *    this list of conditions and the following disclaimer.
- * 
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 
- * 3. The end-user documentation included with the redistribution, if any, must
- *    include  the following  acknowledgment:  "This product includes  software
- *    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
- *    Alternately, this  acknowledgment may  appear in the software itself,  if
- *    and wherever such third-party acknowledgments normally appear.
- * 
- * 4. The names "log4j" and  "Apache Software Foundation"  must not be used to
- *    endorse  or promote  products derived  from this  software without  prior
- *    written permission. For written permission, please contact
- *    apache@apache.org.
- * 
- * 5. Products  derived from this software may not  be called "Apache", nor may
- *    "Apache" appear  in their name,  without prior written permission  of the
- *    Apache Software Foundation.
- * 
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
- * APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
- * DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
- * ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
- * (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- * 
- * This software  consists of voluntary contributions made  by many individuals
- * on  behalf of the Apache Software  Foundation.  For more  information on the 
- * Apache Software Foundation, please see <http://www.apache.org/>.
- *
- */
diff --git a/apps/bogobot/LICENSE.pircbot.txt b/apps/bogobot/LICENSE.pircbot.txt
deleted file mode 100644
index dcfa4c235e830d9b57c04515dd4b5a80e36075e6..0000000000000000000000000000000000000000
--- a/apps/bogobot/LICENSE.pircbot.txt
+++ /dev/null
@@ -1,340 +0,0 @@
-		    GNU GENERAL PUBLIC LICENSE
-		       Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-			    Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-		    GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-			    NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-		     END OF TERMS AND CONDITIONS
-
-	    How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Library General
-Public License instead of this License.
diff --git a/apps/bogobot/bogobot.bat b/apps/bogobot/bogobot.bat
deleted file mode 100644
index 4c17f7f482e27c197de9c467818c96fcd73b5293..0000000000000000000000000000000000000000
--- a/apps/bogobot/bogobot.bat
+++ /dev/null
@@ -1 +0,0 @@
-java -cp .;log4j-1.2.8.jar;pircbot.jar Bogobot
diff --git a/apps/bogobot/bogobot.config b/apps/bogobot/bogobot.config
deleted file mode 100644
index 647ca88bafcddbe42d59d6057b768144d1512bdf..0000000000000000000000000000000000000000
--- a/apps/bogobot/bogobot.config
+++ /dev/null
@@ -1,101 +0,0 @@
-#####
-# Bogobot user configuration
-#####
-
-###
-# The bot's nick and backup nick. You will probably want to register these with
-# the IRC server's NickServ.(a NickServ interface is forthcoming).
-#
-botPrimaryNick=somebot
-botSecondaryNick=somebot_
-
-###
-# The bot's password required by Nickserv service's identify command.
-# You have to register the nickname yourself first, the bot will not.
-#
-botNickservPassword=
-
-###
-# The bot's username. Appears in the whois replies
-#
-botUsername=somebot
-
-#####
-# The bot owner's nick and backup nick. One of these must match the owner's
-# currently-used nick or else remote shutdown will not be possible. You will
-# probably want to register these with the IRC server's NickServ.
-#
-ownerPrimaryNick=somenick
-ownerSecondaryNick=somenick_
-
-###
-# The bot will disconnect and shut down when sent this password via private
-# message (aka query) from either of the owner nicks specified above. DO NOT USE
-# THIS DEFAULT VALUE!
-#
-botShutdownPassword=take off eh
-
-###
-# The server, channel, and port the bot will connect to.
-#
-ircChannel=#i2p-chat
-ircServer=irc.duck.i2p
-ircServerPort=6668
-
-###
-# Set to "true" to enable logging, else "false" (but don't use quotation marks).
-#
-isLoggerEnabled=true
-
-###
-# Restrict logging of joins and parts on the user hostname.
-# Leave empty to log all of them
-# Prepend with a @ for a perfect match
-# Otherwise, specify the required end of the user hostname
-#
-loggedHostnamePattern=@free.duck.i2p
-
-###
-# The prefix to be used for the filenames of logs.
-#
-logFilePrefix=irc.duck.i2p.i2p-chat
-
-###
-# How often the logs should be rotated. Either "daily", "weekly", or "monthly"
-# (but don't use quotation marks).
-#
-logFileRotationInterval=daily
-
-###
-# Set to "true" to enable the regular round-trip delay computation,
-# else "false" (but don't use quotation marks).
-#
-isRoundTripDelayEnabled=false
-
-###
-# How often should the round-trip delay be recorded.
-# (in seconds)
-#
-roundTripDelayPeriod=300
-
-###
-# Set to "true" to enable the userlist command, else "false" (but don't use
-# quotation marks).
-#
-isUserlistCommandEnabled=true
-
-###
-# The userlist trigger command to listen for. It is a good idea to prefix
-# triggers with some non-alphanumeric character in order to avoid accidental
-# trigger use during normal channel conversation. In most cases you will
-# probably want to choose a unique trigger here that no other bots in the
-# channel will respond to.
-#
-userlistCommandTrigger=!who
-
-###
-# The number of seconds to rest after replying to a userlist command issued by
-# a user in the channel. The bot will ignore subsequent userlist commands during
-# this period. This helps prevent flooding.
-#
-commandAntiFloodInterval=60
diff --git a/apps/bogobot/bogobot.sh b/apps/bogobot/bogobot.sh
deleted file mode 100644
index 7da4e2b3d8143cfaf1187e687c311e44a9b0bfbb..0000000000000000000000000000000000000000
--- a/apps/bogobot/bogobot.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-java -cp .:log4j-1.2.8.jar:pircbot.jar Bogobot
diff --git a/apps/bogobot/build-eclipse.xml b/apps/bogobot/build-eclipse.xml
deleted file mode 100644
index ee101d32460ee407704fe80c17c40a76cb1fe53e..0000000000000000000000000000000000000000
--- a/apps/bogobot/build-eclipse.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!-- ********************************************************** -->
-<!-- bogobot - A simple join/part stats logger bot for I2P IRC. -->
-<!--                                                            -->
-<!-- build-eclipse.xml                                          -->
-<!-- 2004 The I2P Project                                       -->
-<!-- http://www.i2p.net                                         -->
-<!-- This code is public domain.                                -->
-<!--                                                            -->
-<!-- authors: hypercubus, oOo                                   -->
-<!-- version 0.4                                                -->
-<!-- ********************************************************** -->
-
-<project basedir="." default="dist" name="Bogobot">
-
-    <!-- init:
-    Create distribution directory if missing and initialize time stamp for
-    archive naming -->
-    <target name="init">
-        <mkdir dir="dist" />
-        <tstamp>
-            <format pattern="yyyy-MM-dd" property="DSTAMP" />
-        </tstamp>
-    </target>
-
-    <!-- dist.bin:
-    Create the binary distribution archive -->
-    <target depends="init" description="Create the binary distribution archive" name="dist.bin">
-        <zip destfile="dist/Bogobot_${DSTAMP}.zip">
-            <zipfileset dir="${basedir}" includes="bogobot.bat bogobot.config Bogobot.class bogobot.sh Bogoparser.class LICENSE_log4j.txt LICENSE_pircbot.txt log4j-1.2.8.jar pircbot.jar" />
-        </zip>
-    </target>
-
-    <!-- dist.source:
-    Create the source distribution archive -->
-    <target depends="init" description="Create the source distribution archive" name="dist.source">
-        <zip destfile="dist/Bogobot_source_${DSTAMP}.zip">
-            <zipfileset dir="${basedir}" includes="bogobot.bat bogobot.config Bogobot.java bogobot.sh Bogoparser.java build.xml build_eclipse.xml LICENSE_log4j.txt LICENSE_pircbot.txt log4j-1.2.8.jar pircbot.jar" />
-        </zip>
-    </target>
-
-    <!-- dist:
-    Create both the binary and source distribution archives -->
-    <target depends="dist.bin,dist.source" description="Create both the binary and source distribution archives" name="dist">
-        <echo message="Successfully created binary and source distribution archives in directory &apos;dist&apos;." />
-    </target>
-
-    <!-- clean:
-    Delete all class files and temporary directories -->
-    <target description="Delete all class files and temporary directories" name="clean">
-        <delete>
-            <fileset dir="${basedir}" includes="**/*.class" />
-        </delete>
-        <echo message="Clean successful." />
-    </target>
-
-</project>
diff --git a/apps/bogobot/build.xml b/apps/bogobot/build.xml
deleted file mode 100644
index 13c0253bfc3872f6de6125d6117f0f5ca90ca0fe..0000000000000000000000000000000000000000
--- a/apps/bogobot/build.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!-- ********************************************************** -->
-<!-- bogobot - A simple join/part stats logger bot for I2P IRC. -->
-<!--                                                            -->
-<!-- build.xml                                                  -->
-<!-- 2004 The I2P Project                                       -->
-<!-- http://www.i2p.net                                         -->
-<!-- This code is public domain.                                -->
-<!--                                                            -->
-<!-- authors: hypercubus, oOo                                   -->
-<!-- version 0.4                                                -->
-<!-- ********************************************************** -->
-
-<project basedir="." default="compile" name="Bogobot">
-
-    <!-- init:
-    Create distribution directory if missing and initialize time stamp for
-    archive naming -->
-    <target name="init">
-        <mkdir dir="dist" />
-        <tstamp>
-            <format pattern="yyyy-MM-dd" property="DSTAMP" />
-        </tstamp>
-    </target>
-
-    <!-- compile:
-    Compile source code -->
-    <target depends="init" description="Compile source code" name="compile">
-        <javac classpath="${basedir};log4j-1.2.8.jar;pircbot.jar" source="1.4" srcdir="." />
-    </target>
-
-    <!-- dist.bin:
-    Create the binary distribution archive -->
-    <target depends="init,compile" description="Create the binary distribution archive" name="dist.bin">
-        <zip destfile="dist/Bogobot_${DSTAMP}.zip">
-            <zipfileset dir="${basedir}" includes="bogobot.bat bogobot.config Bogobot.class Bogobot$BogobotTickTask.class bogobot.sh Bogoparser.class LICENSE_log4j.txt LICENSE_pircbot.txt log4j-1.2.8.jar pircbot.jar" />
-        </zip>
-    </target>
-
-    <!-- dist.source:
-    Create the source distribution archive -->
-    <target depends="init" description="Create the source distribution archive" name="dist.source">
-        <zip destfile="dist/Bogobot_source_${DSTAMP}.zip">
-            <zipfileset dir="${basedir}" includes="bogobot.bat bogobot.config Bogobot.java bogobot.sh Bogoparser.java build.xml build_eclipse.xml LICENSE_log4j.txt LICENSE_pircbot.txt log4j-1.2.8.jar pircbot.jar" />
-        </zip>
-    </target>
-
-    <!-- dist:
-    Create both the binary and source distribution archives -->
-    <target depends="dist.bin,dist.source" description="Create both the binary and source distribution archives" name="dist">
-        <echo message="Successfully created binary and source distribution archives in directory &apos;dist&apos;." />
-    </target>
-
-    <!-- clean:
-    Delete all class files and temporary directories -->
-    <target description="Delete all class files and temporary directories" name="clean">
-        <delete>
-            <fileset dir="${basedir}" includes="**/*.class" />
-        </delete>
-        <echo message="Clean successful." />
-    </target>
-
-</project>
diff --git a/apps/bogobot/log4j-1.2.8.jar b/apps/bogobot/log4j-1.2.8.jar
deleted file mode 100644
index 493a3ccc1361b0d84889798a995a351138826511..0000000000000000000000000000000000000000
Binary files a/apps/bogobot/log4j-1.2.8.jar and /dev/null differ
diff --git a/apps/bogobot/pircbot.jar b/apps/bogobot/pircbot.jar
deleted file mode 100644
index d936d70ca958e5b5ba51da8b067090aad3c56abb..0000000000000000000000000000000000000000
Binary files a/apps/bogobot/pircbot.jar and /dev/null differ
diff --git a/apps/jdom/jdom.jar b/apps/jdom/jdom.jar
deleted file mode 100644
index 288e64cb5c435f34499a58b234c2106f9d9f0783..0000000000000000000000000000000000000000
Binary files a/apps/jdom/jdom.jar and /dev/null differ
diff --git a/apps/jdom/readme.txt b/apps/jdom/readme.txt
deleted file mode 100644
index d360b3c2523ce0bf45f6415e281b25222ba72ebf..0000000000000000000000000000000000000000
--- a/apps/jdom/readme.txt
+++ /dev/null
@@ -1 +0,0 @@
-This is JDOM 1.0 from http://jdom.org/, released under an Apache style license
diff --git a/apps/pants/build.xml b/apps/pants/build.xml
deleted file mode 100644
index 7770dae10039a4d842b0c614a71bc3449d946bf6..0000000000000000000000000000000000000000
--- a/apps/pants/build.xml
+++ /dev/null
@@ -1,236 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  Ports + Ant = Pants, a simple Ant-based package manager
-
-  free (adj.): unencumbered; not under the control of others
-
-  Written by smeghead in 2005 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.
--->
-
-<project basedir="." default="help" name="pants-interface">
-
-<!-- .......................... Global Properties .......................... -->
-
-
-
-<!-- ........................... Internal Tasks ............................ -->
-
-	<target name="-fetchCvs" unless="cvs.source.available" if="using.cvs">
-		<cvs compressionlevel="${cvs.compression.level}"
-			date="${cvs.date}"
-			dest="./distfiles/cvs-src/${pbuild}"
-			failonerror="true"
-			package="${cvs.package}"
-			passfile="${cvs.passfile}"
-			port="${cvs.port}"
-			cvsRoot="${cvs.root}"
-			cvsRsh="${cvs.rsh}"
-			tag="${cvs.tag}"
-		/>
-	</target>
-
-	<target name="-fetchPackage"  unless="using.cvs">
-		<get src="${package.url}"
-			verbose="true"
-			dest="./distfiles"
-		/>
-	</target>
-
-	<target name="-init">
-		<!--
-		  TODO: Create dist/ and working/ folders for each pbuild subdir in case
-		  they've been wiped.
-		-->
-		<loadproperties srcfile="world" />
-		<taskdef name="mergetypedproperties"
-			classname="net.i2p.pants.MergeTypedPropertiesTask"
-			classpath="./lib/pants.jar"
-		/>
-		<mergetypedproperties input="./pbuilds/${pbuild}/pbuild.properties"
-			output="./pbuilds/${pbuild}/merged-properties.temp"
-			booleanList="version.latest.find.regex.canonicaleq,	version.latest.find.regex.caseinsensitive, version.latest.find.regex.comments, version.latest.find.regex.dotall, version.latest.find.regex.multiline, version.latest.find.regex.unicodecase, version.latest.find.regex.unixlines"
-			stringList="cvs.compression.level, cvs.date, cvs.package, cvs.passfile, cvs.port, cvs.root, cvs.rsh, cvs.tag, package.url, version.latest, version.latest.find.url, version.latest.find.regex"
-		/>
-		<loadproperties srcfile="./pbuilds/${pbuild}/merged-properties.temp" />
-		<delete file="./pbuilds/${pbuild}/merged-properties.temp" />
-		<!--
-		  If '-Dpbuild={name}' isn't specified, the 'build', 'fetch', 'update'
-		  and 'version' commands should default to 'world' behavior.
-		-->
-		<antcall target="-setWorld" />
-		<condition property="using.cvs">
-			<or>
-				<equals arg1="CVS" arg2="${version.using.${pbuild}}" />
-				<equals arg1="cvs" arg2="${version.using.${pbuild}}" />
-			</or>
-		</condition>
-		<!--
-		  If 'version.recommended' isn't defined in pbuild.properties, default
-		  to latest available version.
-		-->
-	</target>
-
-	<target name="-setWorld" unless="pbuild">
-		<property name="pbuild" value="world" />
-	</target>
-
-	<target name="-unpackTarBz2">
-		<untar src="${pbuild.package}"
-			compression="bzip2"
-			dest="./${pbuild}/working"
-		/>
-	</target>
-
-	<target name="-unpackTarGzip">
-		<untar src="${pbuild.package}"
-			compression="gzip"
-			dest="./${pbuild}/working"
-		/>
-	</target>
-
-	<target name="-unpackZip">
-		<unzip src="${pbuild.package}" dest="./${pbuild}/working" />
-	</target>
-
-	<target name="-updateCvs" if="using.cvs">
-		<cvs command="update -d"
-			compressionlevel="${compression.level}"
-			date="${cvs.date}"
-			dest="./distfiles/cvs-src"
-			failonerror="true"
-			package="${cvs.package}"
-			passfile="${cvs.passfile}"
-			port="${cvs.port}"
-			cvsRoot="${cvs.root}"
-			cvsRsh="${cvs.rsh}"
-			tag="${cvs.tag}"
-		/>
-	</target>
-
-	<target name="-updateConfirm" if="confirm.update" unless="no.prompts">
-		<input validargs="y,Y,n,N"
-			defaultvalue="n"
-			addproperty="confirm.update.answer">
-You currently have the recommended version installed. A newer
-version will be installed if you continue and this may break some
-applications which depend on this package. Are you sure you want
-to update? [y/N]
-		</input>
-		<condition property="abort.update">
-			<or>
-				<equals arg1="n" arg2="${confirm.update.answer}" />
-				<equals arg1="N" arg2="${confirm.update.answer}" />
-			</or>
-		</condition>
-		<fail if="abort.update">Update aborted.</fail>
-	</target>
-
-	<target name="-versionLatest">
-		<get src="${version.latest.find.url}"
-			dest="version.latest.in.temp"
-			verbose="true"
-		/>
-		<taskdef name="match"
-			classname="net.i2p.pants.MatchTask"
-			classpath="./lib/pants.jar"
-		/>
-		<match input="version.latest.in.temp"
-			output="version.latest.parsed.temp"
-			regex="${version.latest.find.regex}"
-			canonicaleq="${version.latest.find.regex.canonicaleq}"
-			caseinsensitive="${version.latest.find.regex.caseinsensitive}"
-			comments="${version.latest.find.regex.comments}"
-			dotall="${version.latest.find.regex.dotall}"
-			multiline="${version.latest.find.regex.multiline}"
-			unicodecase="${version.latest.find.regex.unicodecase}"
-			unixlines="${version.latest.find.regex.unixlines}"
-		/>
-		<loadproperties srcFile="version.latest.parsed.temp" />
-		<delete file="version.latest.in.temp" />
-		<delete file="version.latest.parsed.temp" />
-		<property name="version.latest" value="${group.1}" />
-	</target>
-
-	<target name="-versionRecommended">
-		<property name="version.recommended" value="x" />
-	</target>
-
-	<target name="-versionUsing">
-		<property name="version.using" value="x" />
-	</target>
-
-<!-- .......................... Public Interface ........................... -->
-
-	<target name="build" depends="-init,fetch"
-		description="Build a pbuild and its dependencies">
-		<ant antfile="pbuild.xml" dir="./pbuilds/${pbuild}" target="clean" />
-		<ant antfile="pbuild.xml" dir="./pbuilds/${pbuild}" target="build" />
-		<!--
-		  Perform a 'clean' on the target first (but not 'distclean')
-		-->
-	</target>
-
-	<target name="fetch" depends="-init"
-		description="Get package only">
-		<antcall target="-fetchPackage" />
-		<antcall target="-fetchCvs" />
-		<copydir dest="./pbuilds/${pbuild}/working"
-			src="./distfiles/cvs-src/${pbuild}"
-		/>
-	</target>
-
-	<target name="help"
-		description="Display usage synopsis">
-		<echo>
-Pants usage:
-
-    ant [--usejikes] [-Dpbuild={name}] [-Dpbuild.version={version}]
-        [-D{property}={value}] [-Dno.prompts=true] build | fetch |
-        help | install | uninstall | update | version
-
-    build        Build a pbuild and its dependencies
-    fetch        Get package only
-    help         Display usage synopsis
-    install      Fetch, build and install a pbuild
-    uninstall    Uninstall a pbuild
-    update       Update pbuild(s) to the latest version(s)
-    version      Display pbuild version information
-		</echo>
-	</target>
-
-	<!--
-	  Install recommended version by default unless 'version' property is set.
-	  Do not install if package is already installed.
-	-->
-	<target name="install" depends="-init, build"
-		description="Install a pbuild">
-		<ant antfile="pbuild.xml" dir="./pbuilds/${pbuild}" target="dist" />
-		<ant antfile="pbuild.xml" dir="./pbuilds/${pbuild}"
-			target="distclean"
-		/>
-	</target>
-
-	<target name="uninstall" depends="-init"
-		description="Uninstall a pbuild" />
-
-	<target name="update" depends="-init"
-		description="Update pbuild(s) to the latest version(s)">
-		<condition property="${confirm.update}">
-			<equals arg1="${version.using}" arg2="${version.recommended}" />
-		</condition>
-		<antcall target="-updateConfirm" />
-	</target>
-
-	<target name="version"
-		depends="-init, -versionRecommended, -versionUsing, -versionLatest"
-		description="Display pbuild version information">
-		<echo message="Latest version: ${version.recommended}" />
-		<echo message="Latest version: ${version.using}" />
-		<echo message="Latest version: ${version.latest}" />
-	</target>
-
-</project>
diff --git a/apps/pants/pants/build.xml b/apps/pants/pants/build.xml
deleted file mode 100644
index 3f8554c0736af8890e1fd6c2c6fad469815ba815..0000000000000000000000000000000000000000
--- a/apps/pants/pants/build.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project basedir="." default="build" name="build-pants">
-
-	<target name="build"
-		description="Build the source">
-		<mkdir dir="./java/build"/>
-		<javac srcdir="./java/src" source="1.3" target="1.3" deprecation="on" destdir="./java/build" />
-	</target>
-
-	<target name="clean"
-		description="Remove all object files">
-		<delete dir="./java/build" />
-		<delete dir="./java/jartemp" />
-	</target>
-
-	<target name="dist" depends="build, jar"
-		description="Create the jar and copy it to ../lib">
-		<copy todir="../lib" file="./java/build/pants.jar" />
-	</target>
-
-	<target name="distclean" depends="clean"
-		description="Remove the jar and all object files" >
-		<delete file="../lib/pants.jar" />
-	</target>
-
-	<target name="jar">
-		<delete dir="./java/jartemp" />
-		<mkdir dir="./java/jartemp" />
-		<copy todir="./java/jartemp">
-			<fileset dir="./java/build" includes="**/*.class" />
-		</copy>
-		<jar basedir="./java/jartemp" jarfile="./java/build/pants.jar">
-			<manifest>
-				<section name="net.i2p.pants">
-					<attribute name="Implementation-Title" value="Pants" />
-					<attribute name="Implementation-Version" value="0.0.1" /> 
-					<attribute name="Implementation-Vendor" value="I2P" />
-					<attribute name="Implementation-Vendor-Id" value="I2P" />
-					<attribute name="Implementation-URL" value="http://www.i2p.net" />
-				</section>
-			</manifest>
-		</jar>
-		<delete dir="./java/jartemp" />
-	</target>
-</project>
diff --git a/apps/pants/pants/java/src/net/i2p/pants/MatchTask.java b/apps/pants/pants/java/src/net/i2p/pants/MatchTask.java
deleted file mode 100644
index 6750e63141ee28f686adc0181d4155ef4fef14d7..0000000000000000000000000000000000000000
--- a/apps/pants/pants/java/src/net/i2p/pants/MatchTask.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Ports + Ant = Pants, a simple Ant-based package manager
- * 
- * free (adj.): unencumbered; not under the control of others
- * 
- * Written by smeghead in 2005 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.
- */
-
-package net.i2p.pants;
-
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.Task;
-
-/**
- * <p>Custom Ant task for matching the contents of a file against a given
- * regular expression and writing any matching groups to a file in
- * <code>java.util.Properties</code> format.
- * </p>
- * <p>Each key in the properties file is named after the number corresponding to
- * its matching group and its value is the contents of the matching group.
- * </p>
- * <p>Regular expressions passed to this task must conform to the specification
- * used by Sun's <code>java.util.regex</code> package and thus are mostly
- * compatible with Perl 5 regular expressions.
- * </p>
- * <p>When calling the <code>match</code> task, the attributes
- * <code>input</code>, <code>output</code>, and <code>regex</code> are required.
- * </p>
- * <p>Optional boolean attributes may be used to toggle various modes for the
- * regular expression engine (all are set to <code>false</code> by default):
- * </p>
- * <table>
- * <tr><td><code>canonicaleq</code></td><td>Enable canonical equivalence</td></tr>
- * <tr><td><code>caseinsensitive</code></td><td>Enable case-insensitive matching</td></tr>
- * <tr><td><code>comments</code></td><td>Permit whitespace and comments in pattern</td></tr>
- * <tr><td><code>dotall</code></td><td>Enable dotall mode</td></tr>
- * <tr><td><code>multiline</code></td><td>Enable multi-line mode</td></tr>
- * <tr><td><code>unicodecase</code></td><td>Enable Unicode-aware case folding</td></tr>
- * <tr><td><code>unixlines</code></td><td>Enable Unix lines mode</td></tr>
- * </table>
- * <p>There is one additional optional boolean attribute,
- * <code>failOnNoMatch</code>. If this attribute is <code>true</code> it causes
- * the <code>match</code> task to throw a
- * <code>org.apache.tools.ant.BuildException</code> and fail if no matches for
- * the regular expression are found. The default value is <code>false</code>,
- * meaning a failed match will simply result in a warning message to
- * <code>STDERR</code> and an empty (0 byte) <code>output</code> file being
- * created. 
- * </p>
- * <p>
- * <h4>Example</h4>
- * </p>
- * <p>Contents of input file <code>letter.txt</code>:
- * <pre>
- *      Dear Alice,
- * 
- *      How's about you and me gettin' together for some anonymous foo action?
- * 
- *      Kisses,
- *      Bob
- * </pre>
- * </p>
- * <p>Ant <code>match</code> task and a <code>taskdef</code> defining it:
- * <pre>
- *      &lt;taskdef name="match" classname="net.i2p.pants.MatchTask" classpath="../../lib/pants.jar" /&gt;
- *      &lt;match input="letter.txt"
- *             output="matches.txt"
- *             regex="about (\S*?) and (\S*?) .+anonymous (\S*?)"
- *             /&gt;
- * </pre>
- * </p>
- * <p>Contents of properties file <code>matches.txt</code> written by this task:
- * <pre>
- *      group.0=about you and me gettin' together for some anonymous foo
- *      group.1=you
- *      group.2=me
- *      group.3=foo
- * </pre>
- * </p>
- * <p>These values can be loaded from <code>matches.txt</code> into Ant
- * properties like so:
- * <pre>
- *      &lt;loadproperties srcFile="matches.txt" /&gt;
- * </pre>
- * </p>
- *  
- * @author smeghead
- */
-public class MatchTask extends Task {
-
-	private boolean _failOnNoMatch;
-	private String  _inputFile;
-	private String  _outputFile;
-	private String  _regex;
-	private int     _regexFlags;
-
-	public void execute() throws BuildException {
-		int               charRead    = 0;
-		FileReader        fileReader  = null;
-		FileWriter        fileWriter  = null;
-		Matcher           matcher     = null;
-		Pattern           pattern     = null;
-		PrintWriter       printWriter = null;
-		StringBuffer      text        = new StringBuffer();
-
-		if (_inputFile == null)
-			throw new BuildException("Error: 'match' task requires 'input' attribute");
-
-		if (_outputFile == null)
-			throw new BuildException("Error: 'match' task requires 'output' attribute");
-
-		if (_regex == null)
-			throw new BuildException("Error: 'match' task requires 'regex' attribute");
-
-		pattern = Pattern.compile(_regex, _regexFlags);
-
-		try {
-			fileReader = new FileReader(_inputFile);
-
-			while ((charRead = fileReader.read()) != -1)
-				text.append((char) charRead);
-
-			fileReader.close();
-			matcher = pattern.matcher(text);
-
-			if (matcher.find()) {
-				printWriter = new PrintWriter(new FileWriter(_outputFile));
-
-				for (int i = 0; i <= matcher.groupCount(); i++)
-					printWriter.println("group." + Integer.toString(i) + "=" + matcher.group(i));
-
-				printWriter.flush();
-				printWriter.close();
-			} else {
-				if (_failOnNoMatch) {
-					throw new BuildException("Error: No matches found in " + _inputFile);
-				} else {
-					System.err.println("Warning: No matches found in " + _inputFile);
-					// Create 0 byte output file.
-					fileWriter = new FileWriter(_outputFile);
-					fileWriter.close();
-				}
-			}
-		} catch (FileNotFoundException fnfe) {
-			throw new BuildException("File " + _inputFile + " not found", fnfe);
-		} catch (IOException ioe) {
-			throw new BuildException(ioe);
-		}
-	}
-
-	public void setCanonicalEq(boolean enableCanonicalEq) {
-		if (enableCanonicalEq)
-			_regexFlags |= Pattern.CANON_EQ;
-	}
-
-	public void setCaseInsensitive(boolean enableCaseInsensitive) {
-		if (enableCaseInsensitive)
-			_regexFlags |= Pattern.CASE_INSENSITIVE;
-	}
-
-	public void setComments(boolean enableComments) {
-		if (enableComments)
-			_regexFlags |= Pattern.COMMENTS;
-	}
-
-	public void setDotall(boolean enableDotall) {
-		if (enableDotall)
-			_regexFlags |= Pattern.DOTALL;
-	}
-
-	public void setFailOnNoMatch(boolean failOnNoMatch) {
-		_failOnNoMatch = failOnNoMatch;
-	}
-
-	public void setInput(String inputFile) {
-		_inputFile = inputFile;
-	}
-
-	public void setMultiLine(boolean enableMultiLine) {
-		if (enableMultiLine)
-			_regexFlags |= Pattern.MULTILINE;
-	}
-
-	public void setOutput(String outputFile) {
-		_outputFile = outputFile;
-	}
-
-	public void setRegex(String regex) {
-		_regex = regex;
-	}
-
-	public void setUnicodeCase(boolean enableUnicodeCase) {
-		if (enableUnicodeCase)
-			_regexFlags |= Pattern.UNICODE_CASE;
-	}
-
-	public void setUnixLines(boolean enableUnixLines) {
-		if (enableUnixLines)
-			_regexFlags |= Pattern.UNIX_LINES;
-	}
-}
diff --git a/apps/pants/pants/java/src/net/i2p/pants/MergeTypedPropertiesTask.java b/apps/pants/pants/java/src/net/i2p/pants/MergeTypedPropertiesTask.java
deleted file mode 100644
index 90e9dcaadb46fcd71e8fcc8b321c7fd0fe10a92a..0000000000000000000000000000000000000000
--- a/apps/pants/pants/java/src/net/i2p/pants/MergeTypedPropertiesTask.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Ports + Ant = Pants, a simple Ant-based package manager
- * 
- * free (adj.): unencumbered; not under the control of others
- * 
- * Written by smeghead in 2005 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.
- */
-
-package net.i2p.pants;
-
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.Enumeration;
-import java.util.Properties;
-import java.util.StringTokenizer;
-
-import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.Task;
-
-/**
- * <p>Custom Ant task for loading properties from a
- * <code>java.util.Properties</code> file then merging them with lists of
- * expected properties. When an expected property is found in the properties
- * file it is set to the value given for it in the file. If an expected property
- * from a list isn't found in the properties file its value will be set to "" or
- * "false", depending on the property's data type.
- * </p>
- * <p>A property's data type is determined by membership in one of two lists
- * which can be passed into an instance of this class: a string-typed list and a
- * boolean-typed list. Values for string-typed properties may be any valid
- * string accepted by <code>java.util.Properties</code>, and values for
- * boolean-typed properties must be either "false" or "true".
- * </p>
- * <p>Lists holding more than one property must be comma-delimited.
- * </p>
- * <p>The output of this class is a temporary <code>java.util.Properties</code>
- * file which is suitable for reading by the standard Ant
- * <code>loadproperties</code> task.
- * </p>
- * <p>Note that if any properties in the given lists have already been defined
- * before the <code>mergetypedproperties</code> task is called, their values
- * cannot be changed since Ant properties are immutable.
- * </p>
- * <h4>Example</h4>
- * </p>
- * <p>Contents of a properties file <code>my.properties</code>:
- * <pre>
- *      some.property.exists=true
- *      hasValue=false
- *      some.property=this is a value
- *      property0=bork bork
- *      propertyX=this property wasn't passed in a list
- * </pre>
- * </p>
- * <p>Ant <code>mergetypedproperties</code> task and a <code>taskdef</code>
- * defining it:
- * <pre>
- *      &lt;taskdef name="mergetypedproperties" classname="net.i2p.pants.MergeTypedPropertiesTask" classpath="../../lib/pants.jar" /&gt;
- *      &lt;mergetypedproperties input="my.properties"
- *             output="merged-properties.temp"
- *             booleanList="some.property.exists,is.valid,hasValue"
- *             stringList="some.property,another.property,property0"
- *             /&gt;
- * </pre>
- * </p>
- * <p>Contents of properties file <code>merged-properties.temp</code> written by this task:
- * <pre>
- *      some.property.exists=true
- *      is.valid=false
- *      hasValue=false
- *      some.property=this is a value
- *      another.property=
- *      property0=bork bork
- *      propertyX=this property wasn't passed in a list
- * </pre>
- * </p>
- * <p>If you don't want this task's output to include properties which weren't
- * in the lists of expected properties, you can set the attribute
- * <code>onlyExpected</code> to <code>true</code>. In the example, this would
- * result in the file <code>merged-properties.temp</code> containing only the
- * following properties:
- * <pre>
- *      some.property.exists=true
- *      is.valid=false
- *      hasValue=false
- *      some.property=this is a value
- *      another.property=
- *      property0=bork bork
- * </pre>
- * </p>
- *  
- * @author smeghead
- */
-public class MergeTypedPropertiesTask extends Task {
-
-	private String     _booleanList   = "";
-	private String     _inputFile;
-	private boolean    _onlyExpected;
-	private String     _outputFile;
-	private Properties _propertiesIn  = new Properties();
-	private Properties _propertiesOut = new Properties();
-	private String     _stringList    = "";
-
-	public void execute() throws BuildException {
-		StringTokenizer strtokBoolean = new StringTokenizer(_booleanList, ",");
-		StringTokenizer strtokString  = new StringTokenizer(_stringList, ",");
-		String          property      = "";
-
-		if (_inputFile == null)
-			throw new BuildException("Error: 'mergetypedproperties' task requires 'input' attribute");
-
-		if (_outputFile == null)
-			throw new BuildException("Error: 'mergetypedproperties' task requires 'output' attribute");
-
-		// Add some type-checking on the list elements
-
-		try {
-			_propertiesIn.load(new FileInputStream(_inputFile));
-
-			while (strtokBoolean.hasMoreTokens())
-				_propertiesOut.setProperty(strtokBoolean.nextToken().trim(), "false");
-
-			while (strtokString.hasMoreTokens())
-				_propertiesOut.setProperty(strtokString.nextToken().trim(), "");
-
-			for (Enumeration enumm = _propertiesIn.elements(); enumm.hasMoreElements(); ) {
-				property = (String) enumm.nextElement();
-
-				if (_onlyExpected && !_propertiesOut.containsKey(property))
-					continue;
-				else
-					_propertiesOut.setProperty(property, _propertiesIn.getProperty(property));
-			}
-
-			_propertiesOut.store(new FileOutputStream(_inputFile), "This is a temporary file. It is safe to delete it.");
-		} catch (IOException ioe) {
-			throw new BuildException(ioe);
-		}
-	}
-
-	public void setBooleanList(String booleanList) {
-		_booleanList = booleanList;
-	}
-
-	public void setInput(String inputFile) {
-		_inputFile = inputFile;
-	}
-
-	public void setOnlyExpected(boolean onlyExpected) {
-		_onlyExpected = onlyExpected;
-	}
-
-	public void setOutput(String outputFile) {
-		_outputFile = outputFile;
-	}
-
-	public void setStringList(String stringList) {
-		_stringList = stringList;
-	}
-}
diff --git a/apps/pants/pants/resources/README b/apps/pants/pants/resources/README
deleted file mode 100644
index a11829f71240475bc5747490a848128db4b6bbb7..0000000000000000000000000000000000000000
--- a/apps/pants/pants/resources/README
+++ /dev/null
@@ -1,116 +0,0 @@
-What is Pants?
---------------
-
-    Pants is an Apache Ant-based package manager for the management of 3rd party
-    dependencies in Java development projects. It's loosely modeled after
-    FreeBSD's Ports and Gentoo Linux's Portage, with two major differences:
-
-    * Pants isn't intended for system-wide package management. It's tailored for
-      per-project 3rd party package management. You will typically have one
-      Pants repository per project and each repository will be located somewhere
-      under your project's root directory. If you're familiar with Ports or
-      Portage, a Pants repository is roughly analogous to /usr/ports or
-      /usr/portage.
-
-    * Pants is extremely portable. It goes anywhere Apache Ant goes.
-
-    Pants takes a modular approach to the standard Ant buildfile, breaking it
-    into 3 files for functionality and convenience:
-
-    1. The Pants public interface, pants/build.xml, provides a single consistent
-       way to access and manipulate dependency packages and relieves some of the
-       developer's burden by providing implementations for some frequently-used
-       and complex Ant operations.
-
-    2. pbuild.xml is a specially-structured and slimmed-down Ant buildfile in
-       which you implement custom handling for a package your project depends
-       on. This is known as the "pbuild" and is roughly analogous to a FreeBSD
-       port or a Gentoo ebuild. A fairly explanatory template for pbuilds,
-       pbuild.template.xml, is provided.
-
-    3. pbuild.properties contains those properties for a specific pbuild which
-       are most likely to change over time. It uses the java.util.Properties
-       format which is more human-friendly for hand-editing than Ant/XML. A
-       fairly explanatory template, pbuild.template.properties, is provided.
-
-    There is one more file that completes the Pants system: the metadata file
-    pants/world is a database for keeping track of all packages managed by Pants
-    for your project.
-
-    Pants automatically handles versioning for your project's dependency
-    packages and keeps track of their recommended versions, currently used
-    versions, and latest available versions. This makes it extremely simple for
-    project developers to switch back and forth between different versions of a
-    dependency, and makes it just as easy to update a dependency. You can even
-    update all your project's Pants-managed packages with a single command.
-
-    Pbuilds are designed to automatically handle the downloading, building,
-    repackaging and deployment of source archives, binary archives, and CVS
-    sources, all in a manner that's completely transparent to the project
-    developer. Pbuilds currently support tar + gzip, tar + bzip2, and zip
-    archives.
-
-    Because it is based on Ant, Pants integrates very well with Ant buildfiles
-    and will fit easily into your project's Ant build framework. However, its
-    interface is simple enough to be called just as easily by more traditional
-    build systems such as GNU Make.
-
-
-Why Should I Use Pants?
------------------------
-
-    There are many applications for Pants, but a few use cases should best serve
-    to illustrate its usefulness:
-
-    1. You have a project that you ship with several 3rd party libraries but the
-       versions you're using are stale. With a single command, Pants can
-       automatically discover the latest release versions for all of these, then
-       download, build, and place the fresh libraries where your project's main
-       build system expects them to be at build time.
-
-    2. You want to test multiple versions of a 3rd party library against your
-       project. Pants only requires you to issue a single command to switch
-       library versions, so can spend more time testing and less time hunting
-       packages down, unpackaging them, symlinking, etc.
-
-    3. Pants is public domain. You can ship it with your project if you need to
-       without having to worry about petty intellectual property or licensing
-       issues.
-
-
-Minimum Requirements
---------------------
-
-    * Apache Ant 1.6.2 or higher is recommended
-
-    * Any Java runtime and operating system that will run Ant
-
-
-Installation
-------------
-
-    Not finished yet.
-
-
-Why the Silly Name?
--------------------
-
-    Ports + Ant = Pants. Any other explanation is purely a product of your
-    twisted imagination.
-
-
-Miscellaneous Pocket Fluff
---------------------------
-
-    Author:  smeghead <smeghead@i2pmail.org> <smeghead@mail.i2p>
-
-    License: No license necessary. This work is released into the public domain.
-
-    Price:   Free! But if you really appreciate Pants, or you're just a sicko,
-             please send me a picture of your worst or most unusual pair of
-             pants so I can add it to the Whirling Hall of Pants on pants.i2p,
-             the official Pants eepsite (that's an anonymous website on I2P--see
-             http://www.i2p.net for more information).
-
-
-$Id$
diff --git a/apps/pants/pants/resources/pbuild.template.properties b/apps/pants/pants/resources/pbuild.template.properties
deleted file mode 100644
index b346816b63c8f3bb3a914f72f1a844d5ab99bce5..0000000000000000000000000000000000000000
--- a/apps/pants/pants/resources/pbuild.template.properties
+++ /dev/null
@@ -1,110 +0,0 @@
-# The properties defined in this file can be overridden on the command line by
-# passing them in as parameters like so:
-#
-#     ant -Dpbuild=myapp -Dversion.recommended=2.0.5 install
-#
-# *** DO NOT DEFINE A PROPERTY BUT LEAVE ITS VALUE BLANK. PANTS WILL BREAK! ***
-
-
-#     Recommended Package Version
-#
-# Set this property's value to the package version you want Pants to use for the
-# pbuild by default. The version string specified must match the version
-# substring from the package's filename if the filename contains a version
-# number.
-#
-# Comment out this property to force use of the latest available version.
-#
-# If the pbuild is CVS-based rather than package-based, this property must be
-# set to 'CVS'.
-#
-# Example:
-#
-# version.recommended=2.0.4
-
-
-#     Latest Package Version
-#
-# There are currently two ways to inform Pants of the latest version number for
-# your package.
-#
-# Method 1: Manually modify the property 'version.latest' to reflect the latest
-#           version number.
-#
-# Method 2: Provide a URL for a page on the package's website and a regular
-#           expression with which to parse it in order to extract the version
-#           number of the latest available package. For this you must define the
-#           properties 'version.latest.find.url', 'version.latest.find.regex',
-#           and any regular expression engine mode flags needed. The pattern
-#           defined must have exactly one capturing group to encapsulate the
-#           version string, otherwise the operation will fail.
-#
-# You may use both methods, in which case the version number specified by Method
-# 1 will be used as the fallback value if Method 2 for some reason is
-# unsuccessful.
-#
-# If neither method is enabled here or they fail to return a valid value to
-# Pants, the 'ant update' operation for this pbuild may exit ungracefully unless
-# the pbuild is CVS-based (none of the version.latest.* properties are used by
-# CVS-based pbuilds).
-#
-# The following is a list of boolean properties for optional mode flags used by
-# the regular expression engine. Set a value of "true" for any you wish to use.
-#
-# version.latest.find.regex.canonicaleq      - Enable canonical equivalence
-# version.latest.find.regex.caseinsensitive  - Enable case-insensitive matching
-# version.latest.find.regex.comments         - Permit whitespace and comments
-# version.latest.find.regex.dotall           - Enable dotall mode
-# version.latest.find.regex.multiline        - Enable multi-line mode
-# version.latest.find.regex.unicodecase      - Enable Unicode-aware case folding
-# version.latest.find.regex.unixlines        - Enable Unix lines mode
-#
-# Examples:
-#
-# version.latest=5.1.2
-# version.latest.find.url=http://sourceforge.net/projects/jetty/
-# version.latest.find.regex=Stable.+?Jetty-(.+?)</A>
-
-
-#     Package URL
-#
-# Specify the URL pointing to the pbuild's package from here. The token
-# '${pbuild.version}' if used will automatically be expanded to the appropriate
-# version string.
-#
-# The package URL property is not used by CVS-based pbuilds.
-#
-# Examples:
-#
-# package.url=ftp://borkbork.se/bork-${pbuild.version}.tar.bz2
-# package.url=http://bork.borkbork.se/bork-${pbuild.version}-src.tar.gz
-
-
-#     CVS Repository
-#
-# The values expected for CVS properties here are the same as those expected by
-# their corresponding Apache Ant 'Cvs' task attributes. For details see:
-#
-#     http://ant.apache.org/manual/CoreTasks/cvs.html
-#
-# Not all of the 'Cvs' task's attributes have corresponding Pants properties.
-# The following is a list of all valid CVS properties for Pants (and their
-# default values if applicable):
-#
-#     cvs.compression.level
-#     cvs.date
-#     cvs.package
-#     cvs.passfile=~/.cvspass
-#     cvs.port=2401
-#     cvs.root
-#     cvs.rsh
-#     cvs.tag
-#
-# Of these, only the 'cvs.root' property is required for CVS-based pbuilds.
-#
-# Examples:
-#
-# cvs.root=:pserver:anoncvs@borkbork.se:/cvsroot/bork
-# cvs.rsh=ssh
-# cvs.package=borkbork
-
diff --git a/apps/pants/pants/resources/pbuild.template.xml b/apps/pants/pants/resources/pbuild.template.xml
deleted file mode 100644
index 17017f85bf0164896dde27a4a0136d4a024f77e6..0000000000000000000000000000000000000000
--- a/apps/pants/pants/resources/pbuild.template.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  This is a template for Pants pbuilds. Pbuilds use standard Apache Ant syntax.
-  For each target in the Public Interface section you must provide either an
-  implementation or a stub. You may also add your own custom tasks and
-  properties to this file. Be careful that none of your custom properties'
-  names clash with the properties defined in pants/build.xml.
--->
-
-<project basedir="." default="build" name="name-of-pbuild-here">
-
-<!-- ....................... Begin Public Interface ........................ -->
-
-	<!--
-	  When this target is called, the pbuild's sources and/or binaries have
-	  already been extracted/copied by Pants into the pbuild's working/
-	  subdirectory. This target must prepare those sources and/or binaries in
-	  the working/ subdirectory into deployable form, for example by building
-	  all necessary classes and jar files.
-	  
-	  This target must not create or modify any files outside the pbuild's
-	  working/ subdirectory. (An automatic sandboxing mechanism should be added
-	  to Pants at some point.) It is however acceptable for a task called by
-	  'builddep' to modify files outside of this pbuild's working/ directory.
-	-->
-	<target name="build" depends="builddep" />
-
-	<!--
-	  Use this to call targets from other pbuilds, Ant buildfiles, Makefiles,
-	  etc. which perform tasks this pbuild's 'build' target depends on. If other
-	  pbuilds are called here, they must be called through the Pants interface
-	  or else it may leave Pants in an inconsistent state.
-	  
-	  Most pbuilds probably won't need to implement this target.
-	-->
-	<target name="builddep" />
-
-	<!--
-	  This target must undo the actions performed by the 'build' target.
-	-->
-	<target name="clean" depends="depclean" />
-
-	<!--
-	  If the 'builddep' target is implemented, this target must be implemented
-	  to undo its actions.
-	-->
-	<target name="depclean" />
-
-	<!--
-	  This target must copy all deployable files generated by the 'build' target
-	  into the pbuild's dist/ subdirectory (for use by other pbuilds or Ant
-	  processes) or to their final deployment locations outside the pants/
-	  directory hierarchy. Note that the latter may require the user to gain
-	  superuser/admin privileges.
-	-->
-	<target name="dist" depends="build" />
-
-	<!--
-	  This target must remove all files from the pbuild's dist/ subdirectory
-	  and final deployment locations, reversing the actions of the 'dist'
-	  target. Note that removal of files from their final deployment locations
-	  may require the user to gain superuser/admin privileges.
-	-->
-	<target name="distclean" depends="clean" />
-
-<!-- ........................ End Public Interface ......................... -->
-
-</project>
diff --git a/apps/pants/pbuilds/fortuna/pbuild.properties b/apps/pants/pbuilds/fortuna/pbuild.properties
deleted file mode 100644
index acfc8b4fa589fa7d8096fcd37af5cb3462260528..0000000000000000000000000000000000000000
--- a/apps/pants/pbuilds/fortuna/pbuild.properties
+++ /dev/null
@@ -1,112 +0,0 @@
-# The properties defined in this file can be overridden on the command line by
-# passing them in as parameters like so:
-#
-#     ant -Dpbuild=myapp -Dversion.recommended=2.0.5 install
-#
-# *** DO NOT DEFINE A PROPERTY BUT LEAVE ITS VALUE BLANK. PANTS WILL BREAK! ***
-
-
-#     Recommended Package Version
-#
-# Set this property's value to the package version you want Pants to use for the
-# pbuild by default. The version string specified must match the version
-# substring from the package's filename if the filename contains a version
-# number.
-#
-# Comment out this property to force use of the latest available version.
-#
-# If the pbuild is CVS-based rather than package-based, this property must be
-# set to 'CVS'.
-#
-# Example:
-#
-# version.recommended=2.0.4
-version.recommended=CVS
-
-#     Latest Package Version
-#
-# There are currently two ways to inform Pants of the latest version number for
-# your package.
-#
-# Method 1: Manually modify the property 'version.latest' to reflect the latest
-#           version number.
-#
-# Method 2: Provide a URL for a page on the package's website and a regular
-#           expression with which to parse it in order to extract the version
-#           number of the latest available package. For this you must define the
-#           properties 'version.latest.find.url', 'version.latest.find.regex',
-#           and any regular expression engine mode flags needed. The pattern
-#           defined must have exactly one capturing group to encapsulate the
-#           version string, otherwise the operation will fail.
-#
-# You may use both methods, in which case the version number specified by Method
-# 1 will be used as the fallback value if Method 2 for some reason is
-# unsuccessful.
-#
-# If neither method is enabled here or they fail to return a valid value to
-# Pants, the 'ant update' operation for this pbuild may exit ungracefully unless
-# the pbuild is CVS-based (none of the version.latest.* properties are used by
-# CVS-based pbuilds).
-#
-# The following is a list of boolean properties for optional mode flags used by
-# the regular expression engine. Set a value of "true" for any you wish to use.
-#
-# version.latest.find.regex.canonicaleq      - Enable canonical equivalence
-# version.latest.find.regex.caseinsensitive  - Enable case-insensitive matching
-# version.latest.find.regex.comments         - Permit whitespace and comments
-# version.latest.find.regex.dotall           - Enable dotall mode
-# version.latest.find.regex.multiline        - Enable multi-line mode
-# version.latest.find.regex.unicodecase      - Enable Unicode-aware case folding
-# version.latest.find.regex.unixlines        - Enable Unix lines mode
-#
-# Examples:
-#
-# version.latest=5.1.2
-# version.latest.find.url=http://sourceforge.net/projects/jetty/
-# version.latest.find.regex=Stable.+?Jetty-(.+?)</A>
-
-
-#     Package URL
-#
-# Specify the URL pointing to the pbuild's package from here. The token
-# '${pbuild.version}' if used will automatically be expanded to the appropriate
-# version string.
-#
-# The package URL property is not used by CVS-based pbuilds.
-#
-# Examples:
-#
-# package.url=ftp://borkbork.se/bork-${pbuild.version}.tar.bz2
-# package.url=http://bork.borkbork.se/bork-${pbuild.version}-src.tar.gz
-
-
-#     CVS Repository
-#
-# The values expected for CVS properties here are the same as those expected by
-# their corresponding Apache Ant 'Cvs' task attributes. For details see:
-#
-#     http://ant.apache.org/manual/CoreTasks/cvs.html
-#
-# Not all of the 'Cvs' task's attributes have corresponding Pants properties.
-# The following is a list of all valid CVS properties for Pants (and their
-# default values if applicable):
-#
-#     cvs.compression.level
-#     cvs.date
-#     cvs.package
-#     cvs.passfile=~/.cvspass
-#     cvs.port=2401
-#     cvs.root
-#     cvs.rsh
-#     cvs.tag
-#
-# Of these, only the 'cvs.root' property is required for CVS-based pbuilds.
-#
-# Examples:
-#
-# cvs.root=:pserver:anoncvs@borkbork.se:/cvsroot/bork
-# cvs.rsh=ssh
-# cvs.package=borkbork
-cvs.root=:ext:anoncvs@savannah.gnu.org:/cvsroot/gnu-crypto
-cvs.rsh=ssh
-cvs.package=gnu-crypto
diff --git a/apps/pants/pbuilds/fortuna/pbuild.xml b/apps/pants/pbuilds/fortuna/pbuild.xml
deleted file mode 100644
index 02a3e8ce5fa0ddc59bb7e12554d75a5bd372c515..0000000000000000000000000000000000000000
--- a/apps/pants/pbuilds/fortuna/pbuild.xml
+++ /dev/null
@@ -1,127 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project basedir="." default="build" name="fortuna-pbuild">
-
-	<property name="gnucrypt.base.dir" value="./working/gnu-crypto" />
-	<property name="gnucrypt.etc.dir" value="${gnucrypt.base.dir}/etc" />
-	<property name="gnucrypt.lib.dir" value="${gnucrypt.base.dir}/lib" />
-	<property name="gnucrypt.object.dir" value="${gnucrypt.base.dir}/classes" />
-	<property name="gnucrypt.base.crypto.object.dir" value="${gnucrypt.object.dir}/gnu/crypto" />
-	<property name="gnucrypt.cipher.object.dir" value="${gnucrypt.base.crypto.object.dir}/cipher" />
-	<property name="gnucrypt.hash.object.dir" value="${gnucrypt.base.crypto.object.dir}/hash" />
-	<property name="gnucrypt.prng.object.dir" value="${gnucrypt.base.crypto.object.dir}/prng" />
-
-	<patternset id="fortuna.files">
-		<include name="${gnucrypt.base.crypto.object.dir}/Registry.class" />
-		<include name="${gnucrypt.prng.object.dir}/Fortuna*.class" />
-		<include name="${gnucrypt.prng.object.dir}/BasePRNG.class" />
-		<include name="${gnucrypt.prng.object.dir}/RandomEventListener.class" />
-		<include name="${gnucrypt.prng.object.dir}/IRandom.class" />
-		<include name="${gnucrypt.cipher.object.dir}/CipherFactory.class" />
-		<include name="${gnucrypt.cipher.object.dir}/IBlockCipher.class" />
-		<include name="${gnucrypt.hash.object.dir}/HashFactory.class" />
-		<include name="${gnucrypt.hash.object.dir}/IMessageDigest.class" />
-	</patternset>
-
-	<!--
-		Add this when Fortuna tests are added to GNU Crypto, else write some
-	-->
-	<target name="-test" />
-
-<!-- ....................... Begin Public Interface ........................ -->
-
-	<!--
-	  When this target is called, the pbuild's sources and/or binaries have
-	  already been extracted/copied by Pants into the pbuild's working/
-	  subdirectory. This target must prepare those sources and/or binaries in
-	  the working/ subdirectory into deployable form, for example by building
-	  all necessary classes and jar files.
-	  
-	  This target must not create or modify any files outside the pbuild's
-	  working/ subdirectory. (An automatic sandboxing mechanism should be added
-	  to Pants at some point.) It is however acceptable for a task called by
-	  'builddep' to modify files outside of this pbuild's working/ directory.
-	-->
-	<target name="build" depends="builddep">
-		<delete dir="./working/build" />
-		<delete dir="./working/jartemp" />
-		<mkdir dir="./working/build" />
-		<mkdir dir="./working/jartemp/${gnucrypt.object.dir}" />
-		<copy todir="./working/jartemp">
-			<fileset dir=".">
-				<patternset refid="fortuna.files" />
-			</fileset>
-		</copy>
-		<jar basedir="./working/jartemp/${gnucrypt.object.dir}" jarfile="./working/build/fortuna.jar">
-			<manifest>
-				<section name="fortuna">
-					<attribute name="Implementation-Title" value="I2P Custom GNU Crypto Fortuna Library" />
-					<attribute name="Implementation-Version" value="CVS HEAD" /> 
-					<attribute name="Implementation-Vendor" value="Free Software Foundation" />
-					<attribute name="Implementation-Vendor-Id" value="FSF" />
-					<attribute name="Implementation-URL" value="http://www.gnu.org/software/gnu-crypto" />
-				</section>
-			</manifest>
-		</jar>
-		<delete dir="./working/jartemp" />
-	</target>
-
-	<!--
-	  Use this to call targets from other pbuilds, Ant buildfiles, Makefiles,
-	  etc. which perform tasks this pbuild's 'build' target depends on. If other
-	  pbuilds are called here, they must be called through the Pants interface
-	  or else it may leave Pants in an inconsistent state.
-	  
-	  Most pbuilds probably won't need to implement this target.
-	-->
-	<target name="builddep">
-		<ant dir="${gnucrypt.base.dir}" target="jar" />
-	</target>
-
-	<!--
-	  This target must undo the actions performed by the 'build' target.
-	-->
-	<target name="clean" depends="depclean">
-		<delete dir="./working/jartemp" />
-	</target>
-
-	<!--
-	  If the 'builddep' target is implemented, this target must be implemented
-	  to undo its actions.
-	-->
-	<target name="depclean">
-		<!--
-			Annoyingly the GNU Crypto distclean task called here doesn't clean
-			*all* derived files from java/gnu-crypto/lib like it should (because
-			a couple of lines are commented out).....
-		-->
-		<ant dir="${gnucrypt.base.dir}" target="distclean" />
-		<!--
-			.....and so we mop up the rest ourselves.
-		-->
-		<delete dir="${gnucrypt.lib.dir}" />
-	</target>
-
-	<!--
-	  This target must copy all deployable files generated by the 'build' target
-	  into the pbuild's dist/ subdirectory (for use by other pbuilds or Ant
-	  processes) or to their final deployment locations outside the pants/
-	  directory hierarchy. Note that the latter may require the user to gain
-	  superuser/admin privileges.
-	-->
-	<target name="dist" depends="build">
-		<copy todir="./dist/fortuna.jar" file="./working/build/fortuna.jar" />
-	</target>
-
-	<!--
-	  This target must remove all files from the pbuild's dist/ subdirectory
-	  and final deployment locations, reversing the actions of the 'dist'
-	  target. Note that removal of files from their final deployment locations
-	  may require the user to gain superuser/admin privileges.
-	-->
-	<target name="distclean" depends="clean">
-		<delete file="./dist/fortuna.jar" />
-	</target>
-
-<!-- ........................ End Public Interface ......................... -->
-
-</project>
diff --git a/apps/pants/pbuilds/jetty/pbuild.properties b/apps/pants/pbuilds/jetty/pbuild.properties
deleted file mode 100644
index c377e129963d58aa3919c105a4a8d1f263e81ff6..0000000000000000000000000000000000000000
--- a/apps/pants/pbuilds/jetty/pbuild.properties
+++ /dev/null
@@ -1,112 +0,0 @@
-# The properties defined in this file can be overridden on the command line by
-# passing them in as parameters like so:
-#
-#     ant -Dpbuild=myapp -Dversion.recommended=2.0.5 install
-#
-# *** DO NOT DEFINE A PROPERTY BUT LEAVE ITS VALUE BLANK. PANTS WILL BREAK! ***
-
-
-#     Recommended Package Version
-#
-# Set this property's value to the package version you want Pants to use for the
-# pbuild by default. The version string specified must match the version
-# substring from the package's filename if the filename contains a version
-# number.
-#
-# Comment out this property to force use of the latest available version.
-#
-# If the pbuild is CVS-based rather than package-based, this property must be
-# set to 'CVS'.
-#
-# Example:
-#
-# version.recommended=2.0.4
-version.recommended=5.1.2
-
-#     Latest Package Version
-#
-# There are currently two ways to inform Pants of the latest version number for
-# your package.
-#
-# Method 1: Manually modify the property 'version.latest' to reflect the latest
-#           version number.
-#
-# Method 2: Provide a URL for a page on the package's website and a regular
-#           expression with which to parse it in order to extract the version
-#           number of the latest available package. For this you must define the
-#           properties 'version.latest.find.url', 'version.latest.find.regex',
-#           and any regular expression engine mode flags needed. The pattern
-#           defined must have exactly one capturing group to encapsulate the
-#           version string, otherwise the operation will fail.
-#
-# You may use both methods, in which case the version number specified by Method
-# 1 will be used as the fallback value if Method 2 for some reason is
-# unsuccessful.
-#
-# If neither method is enabled here or they fail to return a valid value to
-# Pants, the 'ant update' operation for this pbuild may exit ungracefully unless
-# the pbuild is CVS-based (none of the version.latest.* properties are used by
-# CVS-based pbuilds).
-#
-# The following is a list of boolean properties for optional mode flags used by
-# the regular expression engine. Set a value of "true" for any you wish to use.
-#
-# version.latest.find.regex.canonicaleq      - Enable canonical equivalence
-# version.latest.find.regex.caseinsensitive  - Enable case-insensitive matching
-# version.latest.find.regex.comments         - Permit whitespace and comments
-# version.latest.find.regex.dotall           - Enable dotall mode
-# version.latest.find.regex.multiline        - Enable multi-line mode
-# version.latest.find.regex.unicodecase      - Enable Unicode-aware case folding
-# version.latest.find.regex.unixlines        - Enable Unix lines mode
-#
-# Examples:
-#
-# version.latest=5.1.2
-# version.latest.find.url=http://sourceforge.net/projects/jetty/
-# version.latest.find.regex=Stable.+?Jetty-(.+?)</A>
-version.latest=5.1.2
-version.latest.find.url=http://sourceforge.net/projects/jetty/
-version.latest.find.regex=Stable.+?Jetty-(.+?)</A>
-
-#     Package URL
-#
-# Specify the URL pointing to the pbuild's package from here. The token
-# '${pbuild.version}' if used will automatically be expanded to the appropriate
-# version string.
-#
-# The package URL property is not used by CVS-based pbuilds.
-#
-# Examples:
-#
-# package.url=ftp://borkbork.se/bork-${pbuild.version}.tar.bz2
-# package.url=http://bork.borkbork.se/bork-${pbuild.version}-src.tar.gz
-package.url=http://mesh.dl.sourceforge.net/sourceforge/jetty/jetty-${pbuild.version}.zip
-
-#     CVS Repository
-#
-# The values expected for CVS properties here are the same as those expected by
-# their corresponding Apache Ant 'Cvs' task attributes. For details see:
-#
-#     http://ant.apache.org/manual/CoreTasks/cvs.html
-#
-# Not all of the 'Cvs' task's attributes have corresponding Pants properties.
-# The following is a list of all valid CVS properties for Pants (and their
-# default values if applicable):
-#
-#     cvs.compression.level
-#     cvs.date
-#     cvs.package
-#     cvs.passfile=~/.cvspass
-#     cvs.port=2401
-#     cvs.root
-#     cvs.rsh
-#     cvs.tag
-#
-# Of these, only the 'cvs.root' property is required for CVS-based pbuilds.
-#
-# Examples:
-#
-# cvs.root=:pserver:anoncvs@borkbork.se:/cvsroot/bork
-# cvs.rsh=ssh
-# cvs.package=borkbork
-
diff --git a/apps/pants/pbuilds/jetty/pbuild.xml b/apps/pants/pbuilds/jetty/pbuild.xml
deleted file mode 100644
index f103135128309d297965c45ade77d157c4a2b249..0000000000000000000000000000000000000000
--- a/apps/pants/pbuilds/jetty/pbuild.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project basedir="." default="all" name="jetty">
-
-	<!-- make this generic, place variables in properties file -->
-
-	<target name="all" depends="build"
-		description="Run the build target" />
-
-	<target name="assignProperties" if="group.0">
-		<property name="latest.jetty.version" value="${group.1}" />
-		<available property="jetty.package.available" file="jetty-${latest.jetty.version}.zip" />
-		<available property="jetty.package.unpacked.available" file="jettypkg/jetty-${latest.jetty.version}" />
-		<echo message="Properties assigned" />
-	</target>
-
-	<target name="build" depends="init, unpackJettyPackage" if="latest.jetty.version"
-		description="Download latest Jetty package and copy needed libs to jettylib/">
-		<property name="unpack.dir" value="jettypkg/jetty-${latest.jetty.version}" />
-		<copy todir="jettylib" overwrite="true" file="${unpack.dir}/ext/ant.jar" />
-		<copy todir="jettylib" overwrite="true" file="${unpack.dir}/ext/jasper-compiler.jar" />
-		<copy todir="jettylib" overwrite="true" file="${unpack.dir}/ext/jasper-runtime.jar" />
-		<copy todir="jettylib" overwrite="true" file="${unpack.dir}/ext/xercesImpl.jar" />
-		<copy todir="jettylib" overwrite="true" file="${unpack.dir}/ext/xml-apis.jar" />
-		<copy todir="jettylib" overwrite="true" file="${unpack.dir}/extra/lib/org.mortbay.jetty-jdk1.2.jar" />
-		<copy todir="jettylib" overwrite="true" file="${unpack.dir}/lib/javax.servlet.jar" />
-		<copy todir="jettylib" overwrite="true" file="${unpack.dir}/lib/org.mortbay.jetty.jar" />
-		<copy todir="jettylib" overwrite="true">
-			<fileset dir="${unpack.dir}/ext" includes="xmlParserAPIs*.jar" />
-		</copy>
-	</target>
-
-	<target name="builddep"
-		description="Build the custom helper Ant task for this buildfile">
-		<mkdir dir="java/build"/>
-		<javac srcdir="./java/src" source="1.3" target="1.3" deprecation="on" destdir="./java/build" />
-	</target>
-
-	<target name="clean"
-		description="Remove temp files and zip only; jettypkg/ requires manual deletion">
-		<echo message="Not actually deleting the Jetty package directory since it's so large" />
-		<delete>
-			<fileset dir="." includes="*.zip jettytemp.html parsed.temp" />
-		</delete>
-	</target>
-
-	<target name="cleandep"
-		description="Remove custom helper Ant task">
-		<delete dir="java/build" />
-	</target>
-
-	<target name="compile" />
-
-	<target name="distclean" depends="clean"
-		description="Remove temp files, zip and jettylib/ contents" >
-		<delete>
-			<fileset dir="jettylib" includes="*.jar"/>
-		</delete>
-	</target>
-
-	<target name="fetchJettyPackage" if="latest.jetty.version" unless="jetty.package.available">
-		<echo message="The Jetty libs are not necessary for using I2P, but are used by some" />
-		<echo message="applications on top of I2P such as the routerconsole." />
-		<get src="http://mesh.dl.sourceforge.net/sourceforge/jetty/jetty-${latest.jetty.version}.zip" verbose="true" dest="jetty-${latest.jetty.version}.zip" />
-	</target>
-
-	<target name="init" depends="builddep">
-		<echo message="Checking SourceForge for latest Jetty version....." />
-		<get src="http://sourceforge.net/projects/jetty/" dest="jettytemp.html" verbose="true" />
-		<taskdef name="match" classname="net.i2p.pants.MatchTask" classpath="../../lib/pants.jar" />
-		<match input="jettytemp.html"
-			output="parsed.temp"
-			regex="Stable.+?Jetty-(.+?)&lt;/A&gt;"
-			/>
-		<loadproperties srcFile="parsed.temp" />
-		<antcall target="assignProperties" />
-	</target>
-
-	<target name="jar" />
-
-	<target name="showlatest" depends="init"
-		description="Display latest version number for Jetty">
-		<echo message="Latest Jetty version: ${latest.jetty.version}" />
-	</target>
-
-	<target name="unpackJettyPackage" depends="fetchJettyPackage" if="latest.jetty.version" unless="jetty.package.unpacked.available">
-		<mkdir dir="jettypkg" />
-		<unzip src="jetty-${latest.jetty.version}.zip" dest="jettypkg" />
-	</target>
-</project>
diff --git a/apps/pants/world b/apps/pants/world
deleted file mode 100644
index 9d4335e57d0b9246267a6910583718dee824a5c4..0000000000000000000000000000000000000000
--- a/apps/pants/world
+++ /dev/null
@@ -1,2 +0,0 @@
-version.using.fortuna=CVS
-version.using.jetty=5.1.2
diff --git a/apps/q/doc/client.jpg b/apps/q/doc/client.jpg
deleted file mode 100644
index 1d3702b50be4d937cc294f351a02ea9ec1222ee3..0000000000000000000000000000000000000000
Binary files a/apps/q/doc/client.jpg and /dev/null differ
diff --git a/apps/q/doc/diagrams.html b/apps/q/doc/diagrams.html
deleted file mode 100644
index 633dda014e7adff14d98c7ce6005582922f5d9dd..0000000000000000000000000000000000000000
--- a/apps/q/doc/diagrams.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-  <head>
-    <title>Q System Diagrams</title>
-  </head>
-
-  <body>
-    <h1>Q Diagrams</h1>
-    Informal system diagrams of Q network, hubs and clients.
-    <center>
-      <hr>
-      <img src="overall.jpg">
-      <hr>
-      <img src="client.jpg">
-      <hr>
-      <img src="hub.jpg">
-    </center>
-    <hr>
-    
-    <address><a href="mailto:aum@mail.i2p">aum</a></address>
-<!-- Created: Sat Apr 16 17:24:02 NZST 2005 -->
-<!-- hhmts start -->
-Last modified: Mon Apr 18 14:06:02 NZST 2005
-<!-- hhmts end -->
-  </body>
-</html>
diff --git a/apps/q/doc/hub.jpg b/apps/q/doc/hub.jpg
deleted file mode 100644
index 10069e842ce1399b28c36c3d280d931afa040009..0000000000000000000000000000000000000000
Binary files a/apps/q/doc/hub.jpg and /dev/null differ
diff --git a/apps/q/doc/index.html b/apps/q/doc/index.html
deleted file mode 100644
index 2fe9883d8bd17e2055058a0276f66201840d073d..0000000000000000000000000000000000000000
--- a/apps/q/doc/index.html
+++ /dev/null
@@ -1,80 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-  <head>
-    <title>Quartermaster - I2P Distributed File Store</title>
-  </head>
-
-  <body>
-    <center>
-      <h1>Quartermaster<br>an I2P Distributed File Store</h1>
-
-      <h3>STATUS<h3>
-        <i>Whole new (incompatible) version currently in development;
-          ETA for release approx 4-7 days;
-          view screenshots <a href="screenshots.html">here</a>
-        </i>
-        <br>
-        <hr>
-
-        <small>
-          <a href="manual/index.html">User Manual</a> |
-          <a href="spec/index.html">Protocol Spec</a> |
-          <a href="metadata.html">Metadata Spec</a> |
-          <a href="diagrams.html">Q Pr0n (diagrams)</a> |
-          <a href="api/index.html">API Spec</a> |
-          <a href="qnoderefs.txt">qnoderefs.txt</a> |
-          Full Download |
-          Updated jar
-        </small>
-    </center>
-
-    <hr>
-    
-    <h2>Intro</h2>
-
-    Quartermaster, or Q for short, is a distributed file storage framework for I2P.
-
-    <h2>Features</h2>
-
-    <ul>
-      <li>Now features 'QSites' - the Q equivalent of Freenet freesites,
-        static websites which are retrievable even if author is offline</li>
-      <li>Easy web interface - interact with Q (and view/insert QSites)
-        from your web browser</li>
-      <li>Maximum expectations of content retrievability</li>
-      <li>Content security akin to Freenet CHK and SSK keys</li>
-      <li>Powerful, flexible search engine</li>
-      <li>Comfortably accommodates both permanent and transient
-        nodes without significant network disruption (for instance,
-        no flooding of the I2P network with futile
-        calls to offline nodes)</li>
-      <li>Rapid query resolution, due to distributed catalogue
-        mirroring which eliminates all in-network query traffic</li>
-      <li>Modular, extensible architecture</li>
-      <li>Simple interfaces for 3rd-party app developers</li>
-      <li>Is custom-designed and built around I2P, so no duplication of
-        I2P's encryption/anonymity features</li>
-      <li>Simple XML-RPC interface for all inter-node communication, makes it easy to
-        implement user-level clients in any language; also allows alternative
-        implementations of core server and/or client nodes.</li>
-    </ul>
-
-    <hr>
-
-    <h2>Status</h2>
-    
-    Q is presently under development, and a test release is expected soon.
-
-    <hr>
-
-    <h2>Architecture</h2>
-
-    Refer to the <a href="spec/index.html">Protocol Specification</a> for more information.
-
-    <hr>
-<!-- Created: Sat Mar 26 11:09:12 NZST 2005 -->
-<!-- hhmts start -->
-Last modified: Mon Apr 18 18:55:19 NZST 2005
-<!-- hhmts end -->
-  </body>
-</html>
diff --git a/apps/q/doc/manual/index.html b/apps/q/doc/manual/index.html
deleted file mode 100644
index 3b88e8fd7d6556fcffce5090ee5fd7e8c753ad7a..0000000000000000000000000000000000000000
--- a/apps/q/doc/manual/index.html
+++ /dev/null
@@ -1,805 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-  <head>
-    <title>Q User/Programmer Manual</title>
-    <style type="text/css">
-<!--
-td { vertical-align: top; }
-code { font-family: courier, monospace; font-weight: bold }
--->
-    </style>
-  </head>
-
-  <body style="font-family: arial, helvetica, sans-serif">
-    <center>
-      <h1>Q User/Programmer Manual</h1>
-      
-      <i>A brief but hopefully easy guide to installing and using the Q distributed file
-        store within the I2P network</i>
-      
-      <br><br>
-      <i>(Return to <a href="../index.html">Q Homepage</a>)</i>
-      <br>
-      <br>
-      <small>
-        <a href="#intro">Introduction</a> |
-        <a href="#checklist">Checklist</a> |
-        <a href="#serverorclient">Server?orClient?</a> |
-        <a href="#walkthrough">Walkthrough</a> |
-        <a href="#server">Server Nodes</a> |
-        <a href="#qmgr">About QMgr</a> |
-        <a href="#contact">Contact us</a>
-      </small>
-    </center>
-
-    <a name="intro"/>
-
-    <hr>
-
-    <h2>1. Introduction</h2>
-
-    <blockquote>
-      Q is a distributed Peer2Peer file storage/retrieval network that aims to deliver optimal
-      performance by respecting the properties of the I2P network.<br>
-      <br>
-      This manual serves as a 'walkthrough' guide, to take you through the steps from initial
-      download, to everyday usage. It also provides information for the benefit of higher-level
-      client application authors.
-    </blockquote>
-
-    <a name="checklist"/>
-
-    <hr>
-
-    <h2>2. Preliminary Checklist</h2>
-
-    <blockquote>
-      OK, we assume here that you've already cracked the tarball, and are looking at
-      the distribution files.<br>
-      <br>
-      In order to get Q set up and running, you'll need:
-      <ol>
-        <li>An I2P router installed, set up and (permanently or transiently) running</li>
-        <li>Your system shell set up with at the environment variables:
-          <ul>
-            <li><b>CLASSPATH</b> - this should include:
-              <ul>
-                <li>The regular I2P jar files and 3rd party support jar files (eg <b>i2p.jar</b>,
-                  <b>i2ptunnel.jar</b>, <b>streaming.jar</b>,
-                  <b>mstreaming.jar</b>, <b>jbigi.jar</b>)</li>
-                <li>Apache's XML-RPC support jarfile - included in this Q distro as
-                  <b>xmlrpc.jar</b></li>
-                <li>Aum's jarfile <b>aum.jar</b>, which includes Q and all needed support code</li>
-              </ul>
-            </li>
-            <li><b>PATH</b> - your execution search path <b><i>must</i></b> include the directory
-              in which your main java VM execution program (<b>java</b>, or on windows systems,
-              <b>java.exe</b>) resides.<br>
-              <b>NOTE</b> - if <b>java[.exe]</b> is not on your <b>PATH</b>, then Q <i>will
-                not run</i>.</li>
-          </ul>
-      </ol>
-    </blockquote>
-
-    <a name="serverorclient"/>
-
-    <hr>
-
-    <h2>3. Q Server or Q Client?</h2>
-
-    <blockquote>
-      Nearly everyone will want to run a <b>Q Client Node</b>.<br>
-      <Br>
-      It is only client nodes which provide users with full access to the Q network.<br>
-      <br>
-      However, if you have a (near-) permanently running I2P Router, and you're a kind and
-      generous soul, you might <i>also</i> be willing to run a <b>Q Server Node</b> in addition
-      to your <b>Q Client Node</b>.<br>
-      <br>
-      If you do choose to run a server node, you'll be expected to keep it running as near as
-      possible to 24/7. While transience of client nodes - frequent entering and leaving the
-      Q network - causes little or no disruption, transience of server nodes can significantly
-      impair Q's usability for everyone, particularly if this transience occurs frequently amongst
-      more than the smallest percentage of the server node pool.<br>
-      <br>
-      Until you're feeling well "settled in" with Q, your best approach is to just run a
-      client node for now, and add a server node later when you feel ready.<br>
-    </blockquote>
-    
-    <a name="walkthrough"/>
-
-    <hr>
-
-    <h2>4. Q Walkthrough</h2>
-
-    <h3>4.1. Introduction</h3>
-
-    <blockquote>
-      This chapter discusses the deployment and usage of a Q Client Node, and will take you
-      through the steps of:
-      <ol>
-        <li>Double-checking that you've met the installation requirements</li>
-        <li>Launching a Q Client Node</li>
-        <li>Verifying that your Q Client Node is running</li>
-        <li>If your node fails to launch, figuring out why</li>
-        <li>Importing one or more noderefs into your node</li>
-        <li>Observing that your node is discovering other nodes on the network</li>
-        <li>Observing that your node is discovering content on the network</li>
-        <li>Searching for items of content that match chosen criteria</li>
-        <li>Retrieving stuff from the network</li>
-        <li>Inserting stuff to the network</li>
-        <li>Shutting down your client node</li>
-      </ol>
-      Setup and running of Q Server Nodes will be discussed in a later chapter.
-    </blockquote>
-
-    <hr>
-
-    <h3>4.2. Verify Your Q Installation Is Correct</h3>
-
-    <blockquote>
-      Ensure that all the needed I2P jarfiles, as well as <b>xmlrpc.jar</b> and
-      Q's very own <b>aum.jar</b> are correctly listed in your <b>CLASSPATH</b> environment
-      varaible, and your main java launcher is correctly listed in your <b>PATH</b> environment
-      variable.<br>
-      <br>
-      Typically, you will likely copy the jarfiles <b>aum.jar</b> and <b>xmlrpc.jar</b>
-      into the <b>lib/</b> subdirectory of your I2P router installation, along with all
-      the other I2P jar files. Wherever you choose to put these files, make sure they're
-      correctly listed in your <b>CLASSPATH</b>.
-      <br>
-      Also, you'll want to add execute permission to your <b>qmgr</b> (or <b>qmgr.bat</b>)
-      wrapper script, and copy it into one of the directories listed in your <b>PATH</b>
-      environment variable.<br>
-    </blockquote>
-
-    <hr>
-
-    <h3>4.3. Get Familiar With qmgr</h3>
-
-    <blockquote>
-      <b>qmgr</b> (or <b>qmgr.bat</b>) is a convenience wrapper script to save your
-      sore fingers from needless typing. It's just a wrapper which passes arguments
-      to the java command <b><code>java&nbsp;net.i2p.aum.q.QMgr</code></b><br>
-      <br>
-      You can verify you've set up qmgr correctly with the command:
-      <blockquote><code><pre>
-qmgr help</pre></code></blockquote>
-      This displays a brief summary of qmgr commands. On the other hand, the command:
-      <blockquote><code><pre>
-qmgr help verbose</pre></code></blockquote>
-      floods your terminal window with a detailed explanation of all the qmgr commands
-      and their arguments.<br>
-    </blockquote>
-
-    <hr>
-
-    <h3>4.4. Running A Q Client Node For The First Time</h3>
-
-    <blockquote>
-      Provided you've successfully completed the preliminaries, you can launch your
-      Q Client Node with the command:
-      <blockquote><code><pre>
-qmgr start</pre></code></blockquote>
-
-      All going well, you should have a Q Client Node now running in background.
-    </blockquote>
-
-    <hr>
-
-    <h3>4.5. Verify that your Q Client Node is actually Running</h3>
-
-    <blockquote>
-      After typed the <b>qmgr start</b> command, you will see little or no
-      evidence that Q is actually running.<br>
-      <br>
-      You can test if the node is actually up by typing the command:
-      <blockquote><code><pre>
-qmgr status</pre></code></blockquote>
-      If your Q Client Node is running, this <b>status</b> command should produce
-      something like:
-      <blockquote><code><pre>
-Pinging node at '/home/myusername/.quartermaster_client'...
-Node Ping:
-  status=ok
-  numPeers=0
-  dest=-3LQaE215uIYwl-DsirnzXGQBI31EZQj9u~xx45E823WqjN5i2Umi37GPFTWc8KyislDjF37J7jy5newLUp-qrDpY7BZum3bRyTXo3Udl8a3sUjuu4qR5oBEWFfoghQiqDGYDQyJV9Rtz7DEGaKHGlhtoGsAYRXGXEa8a43T2llqZx2fqaXs~836g8t6sLZjryA5A9fpq98nE5lT0hcTalPieFpluJVairZREXpUiAUmGHG7wAIjF6iszXLEHSZ8Qc622Xgwy0d1yrPojL2yhZ64o05aueYcr~xNCiFxYoHyEJO3XYmkx~q-W-mzS3nn6pRevRda74MnX1~3fFDZ0u~OG6cLZoFkWgnxrwrWGFUUVMR87Yz251xMCKJAX6zErcoGjGFpqGZsWxl4~yq7yfkjPnq3GuTxp2cB75bRAOZRIAieqBOVJDEodFYW5amCinu4AxYE7G1ezz4ghqHFe~0yaAdO74Q1XoUny138YT6P33oNOOlISO1cAAAA
-  uptime=4952
-  load=0.0
-  id=6LVZ9-~GgJJ52WUF1fLHt3UnH50TnXSoPQXy7WZ4GA=
-  numLocalItems=47
-  numRemoteItems=2173</pre></code></blockquote>
-
-      If you see something like this, then smile, because Q is now up on your system.<br>
-      <br>
-      If the node launch failed, you might see something like:
-      <blockquote><code><pre>
-Pinging node at '/home/myusername/.quartermaster_client'...
-java.io.IOException: Connection refused
-        at org.apache.xmlrpc.XmlRpcClient$Worker.execute(Unknown Source)
-        at org.apache.xmlrpc.XmlRpcClient.execute(Unknown Source)
-        at net.i2p.aum.q.QMgr.doStatus(QMgr.java:310)
-        at net.i2p.aum.q.QMgr.execute(QMgr.java:813)
-        at net.i2p.aum.q.QMgr.main(QMgr.java:869)
-Failed to ping node</pre></code></blockquote>
-      This indicates that your Q client node has either crashed, or failed to launch in the
-      first place.<br>
-      <br>
-      If you're having trouble like this, you might like to try running your Q client node
-      in foreground, instead of spawning it off into background.<br>
-      <br>
-      The command to run a Q client node in foreground is:
-      <blockquote><code><pre>
-qmgr foreground</pre></code></blockquote>
-      You should see some meaningless startup messages, and no return to your shell prompt.<br>
-
-    </blockquote>
-
-    <hr>
-
-    <h3>4.6. Diversion - Q Storage Directories</h3>
-
-    <blockquote>
-      By default, when you run a Q Client Node, it creates a datastore directory tree
-      at <b>~/.quartermaster_client</b>. (Windows users note - you'll find this directory
-      wherever your user home directory is - this depends on what version of Windows
-      you have installed).<br>
-      <br>
-      Within this directory tree, you should see a file called <b>node.log</b>, which
-      will contain various debug log messages, and can help you to rectify any problems
-      with your Q installation. If you hit a wall and can't rectify the problems
-      yourself, you should send this file to the Q author (aum).<br>
-      <br>
-      It's possible to run your Q node from another directory, by passing that directory
-      as a <b>-dir &lt;path&gt;</b> argument to the
-      <b>qmgr</b> <b>start</b>, <b>foreground</b> and <b>stop</b>
-      commands. See <b>qmgr help verbose</b> for more information.
-    </blockquote>
-
-    <hr>
-
-    <h3>4.7. Importing a Noderef</h3>
-
-    <blockquote>
-      Note from the prior <b>qmgr status</b> command the line:
-      <blockquote><code><pre>
-numPeers=0</pre></code></blockquote>
-      This means that your Q client node is running standalone, and doesn't have any contact
-      with any Q network. As such, your node is effectively useless. We need to hook up
-      your node with other nodes in the Q network.<br>
-      <br>
-      Q doesn't ship with any means for new client nodes to automatically connect to any Q
-      server nodes. This is deliberate.<br>
-      <br>
-      In all likelihood, there will be one 'main' Q network running within I2P, largely
-      based around the author's own Q server node, and most people will likely want to
-      use this Q network. But the author doesn't want to stop other people running their
-      own private Q networks, for whatever purpose has meaning for them.
-
-      <blockquote><i><small>
-        <hr>
-        This is especially relevant for Q as opposed to Freenet. With Freenet, there's
-        no way for a user to know of the existence of any item of content without
-        first being given its 'key'. However, since Q works with published catalogs,
-        any user can know everything that's available on a Q network, which might
-        not be desirable to those wishing to share content in a private situation.<br>
-        <Br>
-        The Q author anticipates, and warmly supports, people running their own
-        private Q networks within I2P, in addition to accessing the mainstream
-        'official' Q network.<br>
-        <br>
-        The way Q is designed and implemented, there is no way for anyone, including
-        Q's author, to know of the existence of anyone else's private Q network.
-        It is beyond the author's control, (and thus arguably the author's
-        legal responsibility), what private Q networks people set up, and what
-        kind of content is trafficked on these networks. This claim of plausible
-        deniability on the part of Q's author parallels that of a hardware retailer
-        denying responsibility for what people do with tools that they purchase.
-        <hr>
-      </small>
-      </i></blockquote>
-
-      Ok, getting back on topic - your brand new virgin Q client node is useless and lonely,
-      and desperately needs some Q server nodes to talk to. So let's hook up your node to
-      the mainstream Q network.<br>
-      <br>
-      You'll need to get one or more 'noderefs' for Q server nodes.<br>
-      <br>
-      There's nothing fancy about a Q noderef. It's just a regular I2P 'destination', with
-      which your Q Client Node can connect with a Q Server Node.<br>
-      <br>
-      A 'semi-official' list of noderefs for the mainstream Q network can be downloaded
-      from the url: <a href="http://aum.i2p/q/qnoderefs.txt">http://aum.i2p/q/qnoderefs.txt</a>.<br>
-      <br>
-      Download this file, save it as (say) <b>qnoderefs.txt</b>. (Alternatively, if you're
-      wanting to subscribe into a private Q network, then get a noderef for at least one
-      of that network's server nodes from someone on that network who trusts you).<br>
-      <br>
-      Import these noderefs into your Q client node via the command:
-      <blockquote><code><pre>
-qmgr addref qnoderefs.txt</pre></code></blockquote>
-      If all goes well, you should see no output from this command, or (possibly) a brief
-      line or two suggesting success.<br>
-      <br>
-      Your client node is now subscribed into the Q network of your choice. Verify this
-      with the command:
-      <blockquote><code><pre>
-qmgr status</pre></code></blockquote>
-      In the output from that command, you should see the <b>numPeers=</b> line showing at least
-      1 peer.<br>
-      <br>
-      If there is more than one Q Server Node on the Q network you've just subscribed to,
-      then your local node should sooner or later discover all these server nodes, and
-      the <b>numPeers</b> value should increase over time.<br>
-      <br>
-      <blockquote>
-        <hr>
-        While Q is in its early development and testing stages, the author may abdicate
-        the mainstream Q network, and publish nodrefs for a whole new mainstream Q network.
-        This will especially happen if the author makes any substantial changes to the
-        inter-node protocol, and/or releases incompatible new versions of Q client/server
-        nodes. Remember that
-        <a href="http://aum.i2p/q/qnoderefs.txt">http://aum.i2p/q/qnoderefs.txt</a> will
-        serve as the authoritative source for noderefs for the mainstream Q network within
-        the mainstream I2P network.
-        <hr>
-      </blockquote>
-
-      When your client node gets its noderefs to a Q network, it will periodically,
-      from then on, retrieve differential peer list and catalog updates from servers
-      it knows about.<br>
-      <br>
-      Even if you only feed your client just one ref for a single server node, it will
-      in time discover all other operating server nodes on that Q network, and will
-      build up a full local catalog of everything that's available on that Q network.<br>
-      <br>
-      Provided that your client is running ok, and has been fed with at least one
-      ref for a live Q network that contains content, then over time, successive:
-      <blockquote><code><pre>
-qmgr status</pre></code></blockquote>
-      commands should report increasing values in the fields:
-      <ul>
-        <li><b>numPeers</b> - number of peers this client node knows about</li>
-        <li><b>numLocalItems</b> - number of locally stored content items, ie items
-          which you have either inserted to, or retrieved from, your client node</li>
-        <li><b>numRemoteItems</b> - number of unique data items which are available
-          on remote server nodes in the Q network, and which can be retrieved through
-          your local client node.</li>
-      </ul>
-
-      <blockquote>
-        <hr>
-
-        <h4>4.7.1. One Big Warning</h4>
-
-        If you are participating in more than one distinct Q network, then <b>do not</b>
-        insert noderefs for different networks into the same running instance of a
-        local Q client, unless you don't plan on inserting content via that client.<br>
-        <Br>
-        For instance, let's say you are participating in two different Q networks:
-        <ul>
-          <li>The 'mainstream' Q netowrk</li>
-          <li>A secret Q network - "My friends' teen angst diaries"</li>
-        </ul>
-        If you get a noderef for both these networks, and insert both of these into the
-        same running Q client node, then this local client node will be transparently
-        connected to both networks.<br>
-        <br>
-        If you only ever plan on retrieving content, and never inserting content, this
-        won't be a problem, except that you won't be able to tell which content
-        resides on the mainstream Q network, and which resides in the secret Q network.<br>
-        <Br>
-        The big problem arises from inserting content. Whenever you insert data through this
-        'contaminated' 
-        Q client node, this node picks 3 different servers to which upload a copy of this
-        data. You won't have any control over whether the data gets inserted to the mainstream
-        Q network, or your secret Q network. You might insert something sensitive, intending it
-        to go only into the secret Q network, where in fact it also ends up in the mainstream
-        network, with consequences you might not want.
-      </blockquote>
-    </blockquote>
-
-    <hr>
-
-    <h3>4.8. Content Data and Metadata</h3>
-
-    <blockquote>
-      Whenever content gets stored on Q, it is actually stored as two separate items:
-      <ul>
-        <li>The <b>raw data</b> - whether a text file, or the raw bytes of image files,
-          audio files etc</li>
-        <li>The <b>metadata</b>, which contains human-readable and machine-readable
-          descriptions of the data</li>
-      </ul>
-      Metadata consists of a set of <b>category=value</b> pairs.<br>
-      <br>
-      Confused yet? Don't worry, I'm confused as well. Let's illustrate this with an
-      example of metadata for an MP3 audio recording:
-      <ul>
-        <li>title=Fight_Last_Thursday.mp3</li>
-        <li>type=audio</li>
-        <li>mimetype=audio/mpeg</li>
-        <li>abstract=upcoming single recorded in our garage last April</li>
-        <li>keywords=grunge,country,indie</li>
-        <li>artist=Ring of Fire</li>
-        <li>size=4379443</li>
-        <li>contact=ring-of-fire@mail.i2p</li>
-        <li>key=blah37blah24-yada23hfhyada</li>
-      </ul>
-      All metadata categories are optional. In fact, you can insert content with no metadata
-      at all.<br>
-      <br>
-      If you fail to provide metadata when inserting an item, a blank set of metadata will
-      be created with at least the following categories:
-      <ul>
-        <li><b>key</b> - the derived key, under which the item will later be retrievable
-          by yourself and others</li>
-        <li><b>title</b> - if not provided at insert time, this will be set to the key</li>
-        <li><b>size</b> - size of the item's raw data, in bytes</li>
-      </ul>
-      Within Q, there is a convention to supply a minimal amount of metadata. While this
-      is not expected or enforced, including all these categories is most strongly
-      recommended. These core categories are:
-      <ul>
-        <li><b>title</b> - a meaningful title for the data item, consisting only of characters
-          which are legal in filenames on all platforms, and which ends with a file extension.</li>
-        <li><b>type</b> - one of a superset of eMule classifiers, such as:
-          <ul>
-            <li><b>text</b> - plain text</li>
-            <li><b>html</b> - HTML content</li>
-            <li><b>image</b> - content is in an image format, such as .png, .jpg, .gif etc</li>
-            <li><b>audio</b> - content is an audio sample, such as .ogg, .mp3, .wav etc</li>
-            <li><b>video</b> - due to the sheer size of video files, and Q's present design,
-              it's unlikely people will be inserting video content anytime soon (unless it's
-              very short)</li>
-            <li><b>archive</b> - packed file collections, such as .tar.gz, .zip, .rar etc</li>
-            <li><b>misc</b> - content does not fit into any of the above categories</li>
-          </ul>
-        </li>
-        <li><b>mimetype</b> - not as important as the <b>type</b> category, but providing
-          this category in your metadata is still strongly encouraged. Value for this category
-          should be one of the standard mimetypes, eg <b>text/html</b>, <b>audio/ogg</b> etc.</li>
-        <li><b>abstract</b> - a short description (<255 characters), intended for human reading</li>
-        <li><b>keywords</b> - a comma-separated list of keywords, intended for
-          machine-readability, should be all lowercase, no spaces</li>
-      </ul>
-      Note that you can supply extra metadata categories in addition to the above, and that
-      people searching for content can search on these extra categories if they know about
-      them.
-    </blockquote>
-
-    <hr>
-
-    <h3>4.9. Searching For Content</h3>
-
-    <blockquote>
-      As mentioned earlier - in constrast with Freenet, local Q nodes build up a complete
-      catalog of all available content on whatever Q network they are connected to.<br>
-      <br>
-      This is a design decision, based on the choice to eliminate query traffic.<br>
-      <br>
-      The author hopes that this will result in a distributed storage network with a
-      high retrievability guarantee, in contrast with freenet which offers no such
-      guarantee.<br>
-      <br>
-      With Freenet, you only ever know of the existence of something if someone tells
-      you about it.<br>
-      <br>
-      But with Q, your local client node builds up a global catalog of everything that's
-      available within the whole network.<br>
-      <br>
-      The QMgr client has a command for searching your Q client node:
-      <blockquote><code><pre>
-qmgr search -m category1=pattern1 category2=pattern2 ...</pre></code></blockquote>
-      For example:
-      <blockquote><code><pre>
-qmgr search -m type=audio artist=Mozart keywords=symphony</pre></code></blockquote>
-      or:
-      <blockquote><code><pre>
-qmgr search -m type=text title="bible|biblical|(Nag Hammadi)" keywords="apocrypha|Magdalene"</pre></code></blockquote>
-      As implied in the latter example, search patterns are regular expressions. This example will
-      locate all text items, whose <b>title</b> metadata category contains one of <b>bible</b>, <b>biblical</b> or <b>Nag&nbsp;Hammadi</b>, <i>and</i> whose <b>keywords</b> category contains either
-      or both the words <b>apocrypha</b> or <b>Magdalene</b>.<br>
-      <br>
-      Please use the search function carefully, otherwise (if and when Q usage grows) you
-      could be inundated with thousands or even millions of entries.<br>
-      <br>
-      If a search turns up nothing, qmgr will simply exit. But if it turns up one or more items,
-      it will the items out one at a time, with the key first, then each metadata entry
-      on an indented line following.
-    </blockquote>
-
-    <hr>
-
-    <h3>4.10. Retrieving Content</h3>
-
-    <blockquote>
-      Now, we're actually going to retrieve something.<br>
-      <br>
-      Presumably, after following the previous section, you will have seen one or more search
-      results come up, with the 'keys' under which the items can be accessed.<br>
-      <br>
-      Now, choose one of the keys, preferably for a short text item. Try either of the following
-      commands:
-      <blockquote><code><pre>
-qmgr get &lt;keystring&gt; something.txt</pre></code></blockquote>
-<i>or</i>:
-      <blockquote><code><pre>
-qmgr get &lt;keystring&gt; &gt; something.txt</pre></code></blockquote>
-      (both have the same effect - the first one explicitly writes to the named file, the second
-      one dumps the raw data to stdout, which we shell-redirect into the file.<br>
-      <br>
-      <b><i>Note - redirection of fetched data to a file via shell is not working at present. Use only
-          the first form till we fix the bug.</i></b>
-
-    </blockquote>
-
-    <hr>
-
-    <h3>4.11. Inserting Content</h3>
-
-    <blockquote>
-      Our last example in this walkthrough relates to inserting content.<br>
-      <br>
-      Firstly, create a small text file with 2-3 lines of text, and save it as (say)
-      myqinsert.txt.<br>
-      <br>
-      Now, think of some metadata to insert along with the file. Or, you can just use
-      the set:
-      <blockquote><code><pre>
-type=text
-keywords=test
-abstract=My simple test of inserting into Q
-title=myqinsert.txt</pre></code></blockquote>
-
-      Now, let's insert the file. Ensure your Q client node is running, then type:
-      <blockquote><code><pre>
-qmgr put myqinsert.txt -m type=text keywords=test title="myqinsert.txt" \
- abstract="My simple test of inserting into Q"</pre></code></blockquote>
-      If all went well, this command should produce half a line of gibberish, followed
-      immediately by your shell prompt, eg:
-      <blockquote><code><pre>
-aRoFC~9MU~pM2C-uCTDBp5B7j79spFD8gUeu~BNkUf0=<b>$</b>
-</pre></code></blockquote>
-      The '$' at the end is your shell prompt, and all the characters before it are the 'key'
-      which was derived from the content you just inserted.<br>
-      <br>
-      To avoid the hassle of copying/pasting the key, you could just add output redirection
-      to the above command, eg:
-      <blockquote><code><pre>
-qmgr put myqinsert.txt -m type=text keywords=test title="myqinsert.txt" \
- abstract="My simple test of inserting into Q" \
- > myqinsert.key</pre></code></blockquote>
-      This will cause the generated key to be written safe and sound into the file
-      <b>myqinsert.key</b>.<br>
-      <br>
-      You can verify that this insert worked by a 'get' command, as in:
-      <blockquote><code><pre>
-qmgr get `cat myqinsert.key` somefilename.ext</pre></code></blockquote>
-      (Note that this won't work on windows because the DOS shell is irredeemably brain-damaged. If
-      you're using Windows, you <b>will</b> have to cut/paste the key.
-    </blockquote>
-
-    <hr>
-
-    <h3>4.12. Shutting Down your Node</h3>
-
-    <blockquote>
-      If you've worked through to here, then congratulations! You've got your Q Client Node set up
-      and working, and ready to meet all your distributed file storage and retrieval needs.<br>
-      <br>
-      You can leave your client node running 24/7 if you want. In fact, we recommend you keep your
-      client node running as much of the time as possible, so that you get prompt catalog updates,
-      and can more quickly stay in touch with new content.<br>
-      <br>
-      However, if you need to shut down your node, the command for doing this is:
-      <blockquote><code><pre>
-qmgr stop</pre></code></blockquote>
-      This command will take a while to complete (since the node has to wait for the I2P
-      java shutdown hooks to complete before it can rest in peace). But once your node is
-      shut down, you can start it up again at any time and pick up where you left off.
-    </blockquote>
-
-    <a name="server"/>
-
-    <hr>
-
-    <h2>5. Running a Q Server Node</h2>
-
-    <h3>5.1. Introduction</h3>
-    <blockquote>
-      This section describes the requirements for, and procedures involved with, running
-      a Q Server Node.<br>
-      <br>
-      We'll use a similar 'walkthrough' style to that which we used in the previous section
-      on client nodes.
-    </blockquote>
-
-    <hr>
-
-    <h3>5.2. Requirements and Choices</h3>
-    <blockquote>
-      Running a Q server is a generous thing to do, and helps substantially with making
-      Q work at its best for everyone. However, please do make sure you can meet some
-      basic requirements:
-      <ul>
-        <li>You are running a permanent (24/7) I2P Router, on a box with at least (say)
-        98% uptime.</li>
-        <li>You have a little bandwidth to spare, and don't mind the extra memory, disk and
-          CPU-usage footprint of running a fulltime Q server node</li>
-        <li>You have already been able to successfully run a Q client node.</li>
-      </ul>
-      Also, please decide whether you want your server node to contribute to the mainstream
-      Q network, or whether you want to create your own private Q network, or join someone 
-      else's private network. Your contribution will be most appreciated, though, if you
-      can run a server within the mainstream Q network.
-    </blockquote>
-    
-    <hr>
-
-    <h3>5.3. Starting Your Server Node</h3>
-
-    <blockquote>
-      Starting up a Q Server node is very similar to starting up a Q client node, except
-      that with the qmgr command line, you must put the keyword arg <b>server</b> before the
-      command word. So the command is:
-      <blockquote><code><pre>
-qmgr server start</pre></code></blockquote>
-      Similar to Q client nodes, you can check the status of a running Q server node with
-      the command:
-      <blockquote><code><pre>
-qmgr server status</pre></code></blockquote>
-      (Note that this command will take longer to complete than with client nodes, because
-      the communication passes through a multi-hop I2P tunnel, rather than just through
-      localhost TCP).<br>
-      <br>
-      If the status command succeeds, then you'll know your new Q Server Node is happily
-      running in background.
-    </blockquote>
-
-    <hr>
-    
-    <h3>5.4. Joining A Q Network</h3>
-
-    <blockquote>
-      When a Q Server node starts up for the first time, it is in a private network
-      all by itself.<br>
-      <br>
-      If you want to link your server into an existing Q network, you'll have to add a
-      noderef for at least one other server on that network. The command to do this
-      is similar to that for subscribing a client node to a network:
-      <blockquote><code><pre>
-qmgr server addref &lt;noderef-file&gt;</pre></code></blockquote>
-      where &lt;noderef-file&gt; is a file into which you've saved the noderef for
-      the network you want to join.
-      <blockquote>
-        <hr><i><small>
-        Recall from the section on client nodes that the authoritative noderefs
-        for the mainstream Q network can be downloaded from 
-        <a href="http://aum.i2p/q/qnoderefs.txt">http://aum.i2p/q/qnoderefs.txt</a>.
-        </small></i><hr>
-      </blockquote>
-      After you've added the noderef, subsequent <b>qmgr server status</b> commands
-      should show <b>numPeers</b> having a value of at least 1 (and growing, as more
-      server nodes come online in the mainstream Q network.)
-
-    </blockquote>
-
-    <hr>
-
-    <h3>5.5. Private Networks - Exporting Your Server's Noderef</h3>
-
-    <blockquote>
-      If you're planning to start your own private Q network, and want to include other
-      server operators in this network, then you'll have to export your server's noderef
-      and make it available to the others you want to invite into your network.<br>
-      <br>
-      The command to export your Q Server noderef is:
-      <blockquote><code><pre>
-qmgr server getref &lt;noderef-file&gt;</pre></code></blockquote>
-      This will extract the <i>I2P Destination</i> of your running server node, and
-      write it into &lt;noderef-file&gt;. You can then privately share this file with
-      others who you want to invite into your private network. Each recipient of
-      this file will do a <b>qmgr server addref &lt;noderef-file&gt;</b> command
-      to import your ref into their servers.<br>
-      <br>
-      Don't forget that if you're running, or participating in, a private Q network, then
-      you'll need to run a separate client for accessing this network, separate from any
-      mainstream Q network client you may already be running.<br>
-      <br>
-      To start this extra client, you'll have to choose a directory where you want this
-      client to reside, a port number you want your client to listen on locally for
-      user commands, and run the command:
-      <blockquote><code><pre>
-qmgr -dir /path/to/my/new/client -port &lt;portnum&gt; start</pre></code></blockquote>
-      You need the <b>-port &lt;portnum&gt;</b> command, because otherwise it'll fail 
-      to launch (if you already have a client node running off the mainstream Q network).<br>
-      <br>
-      This will create, and launch, a new instance of a Q client, accessing your private
-      Q network. Don't forget to import your server's noderef into this client. Also,
-      note that you'll have to use this same <b>-port &lt;portnum&gt;</b> argument when
-      doing any operation on this client instance, such as get, put, status, search.
-
-    </blockquote>
-
-    <a name="qmgr"/>
-
-    <hr>
-
-    <h2>6. About the qmgr Utility</h2>
-
-    qmgr (or, to people fluent in Java, <b>net.i2p.aum.q.QMgr</b>), is just one simple
-    Q client application, that happens to be bundled in with the Q distro.<br>
-    <br>
-    It is by no means the only, or even main facility for accessing the Q network. We
-    anticipate that folks will write all manner of client apps, including fancy GUI
-    apps.<br>
-    <br>
-    Anyway, qmgr does give you a rudimentary yet workable client for basic access
-    to the Q network. Until fancy apps get written, qmgr will have to do.<br>
-    <br>
-    Don't forget that qmgr has very detailed inbuilt help. Run:
-      <blockquote><code><pre>
-qmgr help</pre></code></blockquote>
-    for a quick help summary, or:
-      <blockquote><code><pre>
-qmgr help verbose</pre></code></blockquote>
-    for the 'War and Peace' treatise.<br>
-    <br>
-    <blockquote><hr>
-    One crucial concept to remember with qmgr is that client and server node instances
-    are uniquely identified by the directories at which they reside. If you are running
-    multiple server and/or client instances, you can specify an instance with the
-    <b>-dir &lt;dirpath&gt;</b> option - see the help for details.
-      <hr></blockquote>
-
-    <hr>
-
-    One last note - we strongly discourage any writing of client apps that spawn a qmgr
-    process, pass it arguments and parse its results. This is most definitely a path to
-    pain, since qmgr's shell interface is subject to radical change at any time without
-    notice.<br>
-    <br>
-    qmgr is for human usage, or at most, inclusion in init/at/cron scripts. Please respect
-    this.<br>
-    <br>
-    If you want to write higher-level clients, your best course of action is to use the
-    official client api library, which we anticipate will have versions available in
-    Java, Python, Perl and C++. If you want to write in another language, such as
-    OCaml, Scheme etc, then the existing api lib implementations should serve as an excellent
-    reference to support you in writing a native port for your own language.
-
-    <a name="contact"/>
-
-    <hr>
-
-    <h2>8. Contacting the Author</h2>
-
-    I am <b>aum</b>, and can be reached as <b>aum</b> on in-I2P IRC networks, and also
-    at the in-I2P email address of <b>aum@mail.i2p</b>.<br>
-    <br>
-
-    <hr>
-
-    <center>
-      Return to <a href="../index.html">Q Homepage</a><br>
-      <br>
-      <small>
-        <a href="#intro">Introduction</a> |
-        <a href="#checklist">Checklist</a> |
-        <a href="#serverorclient">Server?orClient?</a> |
-        <a href="#walkthrough">Walkthrough</a> |
-        <a href="#server">Server Nodes</a> |
-        <a href="#qmgr">About QMgr</a> |
-        <a href="#contact">Contact us</a>
-      </small>
-    </center>
-
-    <hr>
-
-<!-- Created: Fri Apr  1 11:03:27 NZST 2005 -->
-<!-- hhmts start -->
-Last modified: Sun Apr  3 20:06:53 NZST 2005
-<!-- hhmts end -->
-  </body>
-</html>
diff --git a/apps/q/doc/manual/notes b/apps/q/doc/manual/notes
deleted file mode 100644
index b75d421a90bc89a42ab5bc94b4e0cc8d0c5cbea9..0000000000000000000000000000000000000000
--- a/apps/q/doc/manual/notes
+++ /dev/null
@@ -1,23 +0,0 @@
-
- rise on each hit:
-
-   dy = (1 - y) / kRise
-
- fall after each time unit:
-
-   dy = y / kFall
-
- fall after time dt:
-
-   dy = - y ** - (dt / kFall)
-
- after the next hit:
-
-   y = y - y ** (- dt / kFall) + (1 - y) / kRise
-
-first attempt at a load measurement algorithm:
-  - kFall is an arbitrary constant which dictates decay rate of load
-    in the absence of hits
-  - kRise is another constant which dictates rise of load with each hit
-  - dt is the time between each hit
-
diff --git a/apps/q/doc/metadata.html b/apps/q/doc/metadata.html
deleted file mode 100644
index ece77e536edaf2d2ef6b487c469501f0e99a9226..0000000000000000000000000000000000000000
--- a/apps/q/doc/metadata.html
+++ /dev/null
@@ -1,372 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-  <head>
-    <title>Q Metadata Specification</title>
-
-    <style type="text/css">
-<!--
-td { vertical-align: top; }
-code { font-family: courier, monospace; font-weight: bolder; font-size:smaller }
--->
-    </style>
-
-  </head>
-
-  <body>
-    <h1>Q Metadata Specification</h1>
-
-    <h2>1. Introduction</h2>
-    
-    This document lists the standard metadata keys for Q data items,
-    discussing the rules of metadata insertion, processing and validation.<br>
-
-    <hr>
-
-    <h3>1.1. Definitions</h3>
-
-    To avoid confusions in terminology, this document will strictly abide the following definitions:
-    <br>
-    <br>
-    <table width=80%  cellspacing=0 cellpadding=4 border=1 align=center>
-        <tr style="font-weight: bold">
-          <td>Term</td>
-          <td>Definition</td>
-        </tr>
-        <tr>
-          <td><code>key</code></td>
-          <td>A metadata category name, technically a <code>key</code> as the word is used with
-            Java <code>Hashtable</code> and Python <code>dict</code> objects.</td>
-        </tr>
-        <tr>
-          <td><code>uri</code></td>
-          <td>A Uniform Resource Indicator for an item of content stored within the Q network.<br>
-            Q URIs have the form: <code>Q:&lt;basename&gt;[,&lt;cryptoKey&gt;][&lt;path&gt;]</code>
-            <br>
-            <br>
-            Some examples:
-            <ul>
-              <li><code>Q:fhvnr3HFSK234khsf90sdh42fsh</code> (a plain hash uri, no cryptoKey)</li>
-              <li><code>Q:e54fhjeo39schr2kcy4osEH478D/files/johnny.mp3</code> (a secure space URI,
-                no cryptoKey)</li>
-              <li><code>Q:vhfh4se987WwfkhwWFEwkh3234S,47fhh2dkhseiyu</code> (a plain hash URI, with
-                a cryptoKey)</li>
-            </td>
-        </tr>
-        <tr>
-          <td><code>basename</code></td>
-          <td>The basic element of a Q uri. This will be a base64-encoded hash - refer below to
-            URI calculation procedures</td>
-        </tr>
-        <tr>
-          <td><code>cryptoKey</code></td>
-          <td>An optional session encryption key for the stored data, encoded as base64.
-            This affords some protection to server node operators, and gives them a level
-            of plausible deniability for whatever gets stored in their server's
-            datastore without their direct human awareness.</td>
-        </tr>
-        <tr>
-          <td><code>path</code></td>
-          <td>Whever an item of content is inserted in <code>secure space</code> mode, this path
-            serves as a pseudo-pathname, and is conceptually similar to the <code>path</code>
-            component in (for example) standard HTTP URLs
-            <code>http://&lt;domainname&gt;[:&lt;port&gt;][&lt;path&gt;]</code>, such as
-            <code>http://slashdot.org/faq/editorial.shtml</code> (whose <code>path</code>
-            is <code>/faq/editorial.shtml</code>).<br>
-            <br>
-            Paths, if not empty, should contain a leading slash ("/").
-            If an application specifies a non-empty <code>path</code> that doesn't begin with a
-            leading '/', a '/' will be automatically prepended by the receiving node.
-            </td>
-        </tr>
-        <tr>
-          <td><code>plain hash</code></td>
-          <td>A mode of inserting items, whereby the security of the resulting URI comes from
-            computing the URI from a hash of the item's data and metadata (and imposing a
-            mathematical barrier against spoofing content under a given URI). Corresponds to
-            Freenet's <code>CHK@</code> keys.</td>
-        </tr>
-        <tr>
-          <td><code>secure space</code></td>
-          <td>A mode of inserting items where the security of the URI is based not on a hash of the
-            item's data and metadata (as with <code>plain hash</code> mode),
-            but on the <code>privateKey</code> provided by the
-            application, and a content signature created from that private key.
-            Corresponds to Freenet's <code>SSK@</code> keys. Within a secure space, you
-            can insert any number of items under different pseudo-pathnames (as is the case
-            with Freenet SSK keys).
-          </li>
-    </table>
-
-    <br><br>
-
-    <hr>
-
-    <h3>2.1. Keys Inserted By Application Before sending <code>putItem</code> RPCs</h3>
-
-    As the heading suggests, this is a list of metadata keys which should be inserted by a
-    Q application prior to invoking a <code>putItem</code> RPC on the local Q client node.<br>
-    <br>
-    <table width=80% cellspacing=0 cellpadding=4 border=1 align=center>
-        <tr style="font-weight: bold">
-          <td>Key</td>
-          <td>Data Type</td>
-          <td>Description</td>
-        </tr>
-        <tr>
-          <td><code>title</code></td>
-          <td>String</td>
-          <td>Optional but strongly recommended. A free-text short description of the item,
-            should be less than 80 characters. The idea is that applications should
-            support a 'view' of catalogue data that shows item titles. (Prior Q convention of
-            titles expressed as valid filename syntax has been abandoned).
-          </td>
-        </tr>
-        <tr>
-          <td><code>path</code></td>
-          <td>String</td>
-          <td>Optional but strongly recommended.
-            A virtual 'pathname' for the item, which should be in valid *nix
-            absolute pathname syntax (beginning with '/', containing no '//', consisting
-            only of alphanumerics, '-', '_', '.' and '/'.<br>
-            <br>
-            In Q web interfaces, the <code>filename</code> component of this path will
-            serve as the recommended filename when downloading/saving the item.<br>
-            <br>
-            If the application also provides a
-            <code>privateKey</code> key, the path
-            is used in conjunction with the private key to generate <code>publicKey</code>
-            and <code>signature</code> keys (see below), and ultimately the final <code>uri</code>
-            under which the item can be retrieved by others.<br>
-            <br>
-            Refer also to <code>mimetype</code> below.
-          </td>
-        </tr>
-        <tr>
-          <td><code>encrypt</code></td>
-          <td>String</td>
-          <td>Optional. If this key is present, and has a value "1", "yes" or "true",
-            this indicates that the application wishes the data to be stored on servers in
-            encrypted form.<br>
-            <br>
-            If this key is present and set to a positive value, the Q node, on receiving the
-            <code>putItem</code> RPC, will:
-            <ol>
-              <li>Generate a random symmetric encryption key</li>
-              <li>Encrypt the item's data using this encryption key</li>
-              <li>Delete the <code>encrypt</code> key from the metadata</li>
-              <li>Enclose a base64 representation of this encryption key in the RPC response
-                it sends back to the application (embedded in the <code>uri</code></li>
-            </ol>
-          </td>
-        </tr>
-        <tr>
-          <td><code>type</code></td>
-          <td>String</td>
-          <td>Optional but strongly recommended. A standard ed2k specifier, one of <code>text html image
-              audio video archive other</code></td>
-        </tr>
-        <tr>
-          <td><code>mimetype</code></td>
-          <td>String</td>
-          <td>Optional but moderately recommended. Mimetype designation of data, eg <code>text/html</code>,
-            <code>image/jpeg</code> etc. If not specified, an attempt will be made to guess
-            a mometype from the value of the <code>path</code> key. If this attempt fails, then
-            this key will be set to <code>application/x-octet-stream</code> by the node receiving
-            the <code>putItem</code> RPC.</td>
-        </tr>
-        <tr>
-          <td><code>keywords</code></td>
-          <td>String</td>
-          <td>Optional but moderately recommended.
-            A set of keywords, under which the inserting app would like this item to be
-            discoverable. Keywords should be entirely lower case and comma-separated. Content
-            inserts should consider keywords carefully, and only use space characters inside
-            keywords when necessary (eg, for flagging a distinctive phrase containing very
-            common words).</td>
-        <tr>
-          <td><code>privateKey</code></td>
-          <td>String</td>
-          <td>Optional. A Base64-encoded signing private key, in cases where the application wishes
-            to insert an item in <code>signed space</code> mode. This can be accompanied by another key,
-            <code>path</code>, indicating a 'path' within the signed space. If 'path'
-            is not given, it will default to '/'.<br>
-            <br>
-            Either way, when a node receives a
-            <code>putItem</code> RPC containing a <code>privateKey</code> in its metadata,
-            it removes this key and replaces it with <code>publicKey</code> and
-            <code>signature</code>.
-          </td>
-        </tr>
-        <tr>
-          <td><code>path</code></td>
-          <td>String</td>
-          <td>Optional. The virtual pathname, within signed space, under which to store the item.
-            This gets ignored and deleted unless the application also provides a
-            <code>privateKey</code> as well. But if the private key is given, the path
-            is used in conjunction with the private key to generate <code>publicKey</code>
-            and <code>signature</code> keys (see below).<br>
-            <code>path</code> should be a 'unix-style pathname', ie, containing only slashes
-            as (pseudo) directory delimiters, and alphanumeric, '-', '_' and '.' characters,
-            and preferably ending in a meaningful file extension such as <code>.html</code>
-          </td>
-        </tr>
-        <tr>
-          <td><code>expiry</code></td>
-          <td>int</td>
-          <td>Unixtime at which the inserted item should expire. When this expiry time
-            is reached, the item won't necessarily be deleted straight away, but may
-            be deleted whenever a node's data store is full.<br>
-            <br>
-            If this is not provided, it will default to a given duration according to
-            the client node's configuration.<br>
-            <br>
-            If it is provided, by an application, then the client node will transparently
-            generate the required 'rent payment' before caching the data item and uploading
-            it to servers.
-          </td>
-        </tr>
-    </table>
-
-    <br><br>
-
-    <hr>
-
-    <h3>2.2. Keys Inserted By Node Upon Receipt Of <code>putItem</code> RPC</h3>
-
-    <table width=80% cellspacing=0 cellpadding=4 border=1 align=center>
-        <tr style="font-weight: bold">
-          <td>Key</td>
-          <td>Data Type</td>
-          <td>Description</td>
-        </tr>
-
-        <tr>
-          <td><code>size</code></td>
-          <td>Integer</td>
-          <td>Size of the data to be inserted, in bytes.</td>
-        </tr>
-        <tr>
-          <td><code>dataHash</code></td>
-          <td>String</td>
-          <td>base64-encoded SHA256 hash of data.</td>
-        </tr>
-        <tr>
-          <td><code>uri</code></td>
-          <td>String</td>
-          <td>This depends on whether the item is being inserted in <i>plain</i> or
-            <i>signed space</i> mode.<br>
-            <br>
-            If inserting in <i>plain</i> mode, then the uri is in the form
-            <code>Q:somebase64hash</code>, where the hash is computed according to
-            the <a href="#plainhash">plain hash calculation procedure</a>.<br>
-            <br>
-            If inserting in <i>signed space</i> mode, then the uri will be in the form
-            <code>Q:somebase64hash/path.ext</code>, where the hash is computed as per
-            the <a href="#signedhash">signed space hash calculation procedure</a>, and
-            the <code>/path.ext</code> is the verbatim value of the app-supplied
-            <code>path</code> key.
-          </td>
-        </tr>
-        <tr>
-          <td><code>publicKey</code></td>
-          <td>String</td>
-          <td>Base64-encoded signing public key. In cases where app provides
-            <code>privateKey</code>,
-            a node will derive the signing public key from the private key,
-            delete the private key from the metadata, and replace it with its corresponding
-            public key
-            key.</td>
-        </tr>
-        <tr>
-          <td><code>signature</code></td>
-          <td>String</td>
-          <td>Base64-encoded signature of <code>path+dataHash</code>, created using
-          the app-provided <code>privateKey</code>.</td>
-        </tr>
-        <tr>
-          <td><code>rent</code></td>
-          <td>String</td>
-          <td>A rent payment for the data's accommodation on the server.<br>
-            Intention is to support a variety of payment tokens. Initially, the
-            only acceptable form of payment will be a hashcash-like token,
-            in the form <code>hashcash:base64string</code>. The <code>hashcash:</code>
-            prefix indicates that this payment is in hashcash currency, in which case
-            the <code>base64String</code> should decode to a 16-byte string whose
-            SHA256 hash partially collides with <code>dataHash</code>.
-            The greater the number of bits in the collision,
-            the longer the data's accommodation will be 'paid up for'.<br>
-            <br>
-            If this key is already present, a Q node will verify the hashcash,
-            and adjust the <code>expiry</code> key value to the time the item's accommodation
-            is paid up till.<br>
-            <br>
-            If the key is not present:
-            <ul>
-              <li>A client node will generate a value for this key with enough collision bits
-                to pay the accommodation up till the given app-specified <code>expiry</code> date.</li>
-              <li>A server node will grant temporary free accommodation, and adjust the <code>expiry</code>
-                key to the end of the free accommodation period.</li>
-            </ul>
-          </td>
-        </tr>
-    </table>
-
-    <br><br>
-
-    <a name="plainhash"/>
-
-    <hr>
-
-    <h2>3. URI Determination Procedures</h2>
-
-    <h3>3.1. Plain Hash URI Calculation Procedure</h3>
-
-    When items are inserted in <code>plain</code> mode, the final URI is determined from
-    a hash of the data and metadata. Security of the item is based on the mathematical difficulty
-    of creating an arbitrary data+metadata set whose hash collides with the target URI.<br>
-    <br>
-    Specifically, the recipe for calculating plain hash URIs is:
-    <ol>
-      <li>If the key <code>size</code> is missing, set this to the size of the data,
-        in bytes</li>
-      <li>If the key <code>dataHash</code> is missing, set this to the base64-encoded
-        SHA256(data)</li>
-      <li>If the key <code>title</code> is missing, set this to the value of <code>dataHash</code></li>
-      <li>From the metadata, create a set of strings, each in the form <code>key=value</code>,
-        where each line contains a metadata <code>key</code> and its <code>value</code>, and
-        is terminated by an ASCII linefeed (\n, 0x10).</li>
-      <li>Ensure that key <code>uri</code> is omitted</li>
-      <li>Sort the strings into ascending ASCII sort order</li>
-      <li>Concatenate the strings together into one big string</li>
-      <li>Calculate the SHA256 hash of this string</li>
-      <li>Encode the hash into Base64</li>
-      <li>Prepend the string <code>Q:</code> to this</li>
-    </ol>
-
-    <a name="signedhash"/>
-
-    <hr>
-
-    <h3>3.2. Signed Space URI Calculation Procedure</h3>
-
-    This is much simpler than determining plain hash URI, since the security of the URI
-    is based not on hashes of data and metadata, but on the cryptographic <code>privateKey</code>
-    given by the application.<br>
-    <br>
-    Calculation recipe for Signed Space URIs is:
-    <ol>
-      <li>Calculate the SHA256 hash of the private key's binary data (not its base64 representation)</li>
-      <li>Encode this hash into base64, dropping any trailing '=' characters</li>
-      <li>Append to this the value of metadata item <code>path</code> (recall that <code>path</code>,
-        if not empty, must begin with a '/')</li>
-      <li>Prepend the string <code>Q:</code> to this</li>
-    </ol>
-    The resulting URI then is in the form <code>Q:pubkeyHash/path.ext</code>
-
-    <hr>
-<!-- Created: Tue Apr  5 00:56:45 NZST 2005 -->
-<!-- hhmts start -->
-Last modified: Wed Apr  6 00:36:37 NZST 2005
-<!-- hhmts end -->
-  </body>
-</html>
diff --git a/apps/q/doc/overall.jpg b/apps/q/doc/overall.jpg
deleted file mode 100644
index 16441f9104e6f25137e9097173255f2c6f753d84..0000000000000000000000000000000000000000
Binary files a/apps/q/doc/overall.jpg and /dev/null differ
diff --git a/apps/q/doc/qnoderefs.txt b/apps/q/doc/qnoderefs.txt
deleted file mode 100644
index d194dd65b5a218f2c251d9c165fe9d07bfb2631a..0000000000000000000000000000000000000000
--- a/apps/q/doc/qnoderefs.txt
+++ /dev/null
@@ -1 +0,0 @@
-rxvXpHKfWGWsql4PJaHglAERSUYyrdKKAzK6jPHT4QXRf9jgcVd4mInq0j6H4inVOzT9dG4L6c9GrlQwe4ysUm5jSTyZemxiZpQDCAazsoRzNDv6gevA40J6uGl10JtVtOjqXW8Ej0JUKubz88g~ogPb1h4Xibc-RrtqrvsJebg5xYFkLlnr7DxDtiWzIMRSZ9Ri2P~eq0SwZzd81tvASPj5fb3nySHeABAuY8HrNu0gqRLjeayDpd3OK1ogrxf1lMvfutn5pnLrlVcvKHa~6rNWWGSulsuEYWtpUd4Itj9aKqIgF9ES7RF77Z73W1f6NRTHO48ZLyLLaKVLjDIsHQP-0mOevszcPjFWtheqRKvT2D28WEMpVC-mPtfw91BkdgBa3pwWhwG~7KIhvWhGs8bj2NOKkqrwYU7xhNVaHdDDkzv4gsweCutHNiiCF~4yL54WzCIfSKDjcHjQxxVkh2NKeaItzgw9E~mPAKNZD22X~2oAuuL9i~0lldEV1ddUAAAA
\ No newline at end of file
diff --git a/apps/q/doc/screenshot-home.jpg b/apps/q/doc/screenshot-home.jpg
deleted file mode 100644
index 99eb41cd4f2a0e5b56cea4f2d4b5b34bd8dba177..0000000000000000000000000000000000000000
Binary files a/apps/q/doc/screenshot-home.jpg and /dev/null differ
diff --git a/apps/q/doc/screenshot-iewarn.jpg b/apps/q/doc/screenshot-iewarn.jpg
deleted file mode 100644
index b9b8d1f957f962259851dd35f7bd5cfb691640c7..0000000000000000000000000000000000000000
Binary files a/apps/q/doc/screenshot-iewarn.jpg and /dev/null differ
diff --git a/apps/q/doc/screenshot-qsite.jpg b/apps/q/doc/screenshot-qsite.jpg
deleted file mode 100644
index ba1e9c5bc65d117f84c63ce95b1b105ec5387db0..0000000000000000000000000000000000000000
Binary files a/apps/q/doc/screenshot-qsite.jpg and /dev/null differ
diff --git a/apps/q/doc/screenshot-search.jpg b/apps/q/doc/screenshot-search.jpg
deleted file mode 100644
index cbba0a7c487e5721b27037b93c786d70a23bff00..0000000000000000000000000000000000000000
Binary files a/apps/q/doc/screenshot-search.jpg and /dev/null differ
diff --git a/apps/q/doc/screenshot.jpg b/apps/q/doc/screenshot.jpg
deleted file mode 100644
index fac0c886a880c1f828cf73fe6d84fde4f2ab1bea..0000000000000000000000000000000000000000
Binary files a/apps/q/doc/screenshot.jpg and /dev/null differ
diff --git a/apps/q/doc/screenshot.png b/apps/q/doc/screenshot.png
deleted file mode 100644
index 4115a84e88968a23bf99a1179379845334daefcc..0000000000000000000000000000000000000000
Binary files a/apps/q/doc/screenshot.png and /dev/null differ
diff --git a/apps/q/doc/screenshots.html b/apps/q/doc/screenshots.html
deleted file mode 100644
index 6f003a12bc53a96fc2fec9f89978af8b68b5f8fb..0000000000000000000000000000000000000000
--- a/apps/q/doc/screenshots.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-  <head>
-    <title>Q Screenshots</title>
-  </head>
-
-  <body>
-    <h1>Q Screenshots</h1>
-
-    <ul>
-      <li><a href="screenshot-search.jpg">Search Screen</li>
-      <li><a href="screenshot-qsite.jpg">QSite Insertion Form</li>
-      <li><a href="screenshot-iewarn.jpg">Q Security Features</li>
-    </ul>
-
-    <hr>
-    <address><a href="mailto:aum@mail.i2p">aum</a></address>
-<!-- Created: Sat Apr 16 17:24:02 NZST 2005 -->
-<!-- hhmts start -->
-Last modified: Mon Apr 18 14:06:02 NZST 2005
-<!-- hhmts end -->
-  </body>
-</html>
diff --git a/apps/q/doc/spec/index.html b/apps/q/doc/spec/index.html
deleted file mode 100644
index 854ee838824ac954b3a0dfcbdba9f1afc88b8dfd..0000000000000000000000000000000000000000
--- a/apps/q/doc/spec/index.html
+++ /dev/null
@@ -1,1460 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-  <head>
-    <title>Q Protocol Specification</title>
-
-    <style type="text/css">
-<!--
-td { vertical-align: top; }
--->
-    </style>
-  </head>
-
-
-
-  <body style="font-family: arial, helvetica, sans-serif">
-    <center>
-      <h1>Q Protocol Specification</h1>
-
-      (first draft by aum)<br>
-      <br>
-      Return to <a href="../index.html">Q Homepage</a><br>
-      <br>
-    <small>
-        <a href="#intro">Introduction</a> |
-        <a href="#xmlrpc">XML-RPC</a> |
-        <a href="#arch">Architecture</a> |
-        <a href="#commands">Commands</a> |
-        <a href="#examples">Example&nbsp;Code</a> |
-        <a href="#metadata">Metadata</a> |
-        <a href="#security">Security</a> |
-        <a href="#contact">Contact us</a>
-      </small>
-
-    </center>
-
-    <a name="intro"></a>
-
-    <hr>
-
-    <h2>1. Introduction</h2>
-
-    This document describes details of the interfaces between the various entities
-    in the Q network - <i>server nodes</i>, <i>client nodes</i> and <i>client applications</i>.<br>
-    <br>
-    Purpose is to:
-    <ul>
-      <li>Assist with people writing user client applications, such as GUI apps, command-line
-        apps, or integrate Q in to existing apps.
-      </li>
-
-      <li>Permit alternative implementations of any of these entities, in any
-        programming language.
-      </li>
-
-      <li>Help interested parties to gain a quick understanding of Q's architecture,
-        perhaps with a view to contributing ideas for improvement.</li>
-    </ul>
-
-        <a name="xmlrpc"></a>
-
-    <hr>
-
-    <h2>2. XML-RPC Interface</h2>
-
-    <h3>2.1. WTF? All those ugly complicated angle-brackets?!?</h3>
-
-    If you haven't come across XML-RPC before, the whole concept might seem frightening, like
-    you've gotta write thousands of lines of code for parsing and encoding XML, and
-    negotiate some mind-numbingly complex multi-layered protocol.<br>
-    <br>
-    This is most certainly not the case. XML-RPC libraries are <i>way simple</i> to use.<br>
-    <br>
-    XML-RPC client and server libraries are available for all major (and most minor)
-    programming languages, and are structured in a way that hides all the intricate
-    details and presents an extremely simple and quickly learnable API over the top.
-
-    <hr>
-
-    <h3>2.2. Why XML-RPC??</h3>
-
-    I've chosen XML-RPC as the node interface framework because:
-    <ul>
-      <li>It's easy and quick to learn, regardless of programming language</li>
-      <li>It's supported by free libraries in all major programming languages</li>
-      <li>It avoids the maintenance problems of home-brew interfaces (people writing
-        implementations in several languages, some falling into disuse then breaking)</li>
-      <li>It reduces the opportunity for writing vulnerable client code (compare to writing
-        raw socket handlers in C, and inadvertently opening oneself up to buffer
-        overruns etc)</li>
-      <li>It allows for rapid client development</li>
-    </ul>
-
-      <a name="arch"></a>
-
-    <hr>
-
-    <h2>3. Architectural Overview</h2>
-
-    The Q network is structured as a two-level hierarchy of <i>server nodes</i> and
-    <i>client nodes</i>. Additionally, <i>client applications</i> are run by users, and
-    form the human interface to Q.<br>
-    <br>
-    Let's quickly overview the difference between these three entities:
-    <ul>
-      <li>Server nodes:
-        <ul>
-          <li>Are exptected to stay up all or most of the time</li>
-          <li>Are suited for running on permanently-up I2P routers</li>
-          <li>Run an XML-RPC server, listening exclusively within the I2P network for
-            commands from other peer <i>server nodes</i> as well as from <i>client
-              nodes</i></li>
-          <li>Run XML-RPC clients, for sending commands via I2P to other <i>server nodes</i></li>
-          <li>When joining the network, announce themselves as peers to
-            other <i>server nodes</i></li>
-          <li>Usually have no direct contact with <i>client applications</i></li>
-          <li>Receive and execute commands from <i>client nodes</i>, as well as
-            from other peer <i>server nodes</i>.</li>
-          <li>Will never send commands to <i>client nodes</i>.</li>
-          <li>Store content, which is served up by request to <i>client nodes</i></li>
-          <li>Send catalogues of their stored content on request to <i>client nodes</i></li>
-          <li>Store lists of their known peer <i>server nodes</i>, and send these lists
-            on request to <i>client nodes</i>
-          <li>Manage load by advising <i>client nodes</i>, and peer <i>server nodes</i>,
-            in command replies, of the next advisable time for contact</li>
-          <li>Should preferably be implemented in platform-independent code</li>
-        </ul>
-      </li>
-    <br>
-
-    <li>Client nodes:
-      <ul>
-        <li>May run as continuously or as intermittently as desired without causing
-          disruption to the network</li>
-        <li>Run an XML-RPC server, listening exclusively within the user's local
-          TCP/IP network (usually a localhost port), as opposed to <i>server nodes</i>
-          which run their XML-RPC server listening within I2P</li>
-        <li>Run XML-RPC clients, for sending commands via I2P to <i>server nodes</i></li>
-        <li>Never announce themselves as peers to <i>server nodes</i></li>
-        <li>Never have contact with other <i>client nodes</i>
-        <li>Are suited for use over permanent <i>or</i> transient I2P routers</li>
-        <li>Periodically contact servers requesting differential updates to
-          content catalogues, as well as peer lists. From this info, they maintain
-          a local mirror of what's available globally</li>
-        <li>When receiving any command reply from a given server, are expected to
-          honour the <i>next advised contact time</i> specified by that server</li>
-        <li>Form the official point of access to the Q network for <i>client
-            applications</i></li>
-        <li>Should preferably be implemented in platform-independent code</li>
-      </ul>
-    </li>
-    <br>
-
-    <li>Client applications:
-      <ul>
-        <li>Form the point of human (or third-party program) access to the Q network</li>
-        <li>Offer the user a means of searching for content, inserting content and
-          retrieving content</li>
-        <li>Include GUI apps, CLI apps, web apps, and apps with other user or program
-          interfaces.</li>
-        <li>Usually never run an XML-RPC server at all</li>
-        <li>Run a single XML-RPC client, for sending commands via TCP to a
-          local <i>client node</i></li>
-        <li>Are implemented and maintained separately to the core Q framework, though
-          at any time might be included in official Q distributions</li>
-        <li>Can be freely implemented in platform-independent or platform-dependent
-          code. For instance, Macintosh-only, or Windows-only implementations are
-          perfectly acceptable (but not <i>quite</i> as welcome as platform-independent
-          implementations)</li>
-      </ul>
-    </li>
-
-  </ul>
-
-      <a name="commands"></a>
-
-    <hr>
-
-    <h2>4. Q Command Interface Description</h2>
-
-    <h3>4.1. Overview</h3>
-
-    As mentioned earlier, communication between all Q entities takes place via an
-    XML-RPC mechanism.<br>
-    <br>
-    This chapter describes the actual primitives which are supported by both <i>server
-      nodes</i> and <i>client nodes</i>.<br>
-    <br>
-    Although the primitives are the same for both server and client, the way they are actioned
-    internally may vary.<br>
-    <blockquote>
-      <small>
-        <i>For example, with the <b>getItem</b> primitive, server nodes will only look in
-          their local content store for the item, returning either that item's data and
-          metadata, or a failure reply. On the other hand, client nodes will try their
-          local content store first, and if the item is not found, will look in their
-          peer catalogues. If the item is found in a peer catalogue, the client node will
-          then on-send <b>getItem</b> calls to all server nodes believed to hold that item,
-          until or unless it retrieves a verifiable copy of that item</i>
-      </small>
-    </blockquote>
-
-    <hr>
-
-    <h3>4.2. XML-RPC Data Types</h3>
-
-    <blockquote>
-      <blockquote>
-        <small><i>
-        It's possibly a good idea here to get a hold of the XML-RPC library for
-        your favourite programming language, as well as the manual, and look up
-        the description of data types. Also, if you're especially keen,
-        you might like to read up on XML-RPC in general:
-        <ul>
-          <li><a href="http://ontosys.com/xml-rpc/">XML-RPC Information</li>
-          <li><a href="http://xmlrpc-c.sourceforge.net/xmlrpc-howto/xmlrpc-howto.html">XML-RPC
-              Howto</a></li>
-        </ul>
-        </i></small>
-      </blockquote>
-    </blockquote>
-
-    XML-RPC supports a canonical set of data types, which are seamlessly integrated into
-    all its high level language implementations. A quick overview of the XML-RPC data types
-    used in Q appears below.<br>
-    <br>
-
-    <table width=70% cellspacing=0 cellpadding=4 align=center border=1>
-        <tr>
-          <td><b>XML-RPC Data Type</b></td>
-          <td><b>Description</b></td>
-        </tr>
-
-        <tr>
-          <td>int</td>
-          <td>Plain 32-bit integer</td>
-        </tr>
-
-        <tr>
-          <td>string</td>
-          <td>Sequence of ASCII bytes, viewed as <b>java.lang.String</b> objects in java, and <b>str</b>
-            objects (strings) in Python.
-            Note that ASCII control chars, and high-bit-set chars, are highly illegal and will
-            cause failure.</td>
-        </tr>
-
-        <tr>
-          <td>binary data</td>
-          <td>Raw binary data, viewed as <b>byte []</b> in java, and <b>xmlrpclib.Binary</b> objects
-            in Python. This is the format used for raw content data.</td>
-        <tr>
-          <td>list</td>
-          <td>Sequence of objects, viewed as <b>java.util.Vector</b> in java, and <b>list</b> objects in Python.
-          </td>
-        </tr>
-
-        <tr>
-          <td>struct</td>
-          <td>An unordered set of (key, value) pairs.
-            Represented as <b>java.util.Hashtable</b> objects in java, and
-            <b>dict</b> objects in Python, (<b>associative array</b> in perl, ...)</td>
-        </tr>
-
-    </table>
-
-    <hr>
-
-    <h3>4.3. General Command/Response Format</h3>
-
-    With Q's XML-RPC usage, all commands are a sequence of zero or more arguments. All
-    responses are a <b>struct</b> with at least the key <b>status</b>, whose value, a
-    string, is one of:
-    <ul>
-      <li><b>"ok"</b> - the command was successful; any additional data is included
-        under other keys, depending on the command</li>
-      <li><b>"error"</b> - the command failed, and an additional key <b>error</b>
-        contains a terse description of the error</li>
-    </ul>
-    Note that all commands are also implemented with an alternative entry point, one which
-    takes a single Hashtable (struct/dict/assoc-array) argument. Refer to the javadocs for
-    further info: 
-
-    <hr>
-
-    <h4>4.4. Exceptions - XML-RPC and Otherwise</h4>
-    <blockquote>
-      In certain cases, XML-RPC calls to Q nodes may return an exception.<br>
-      <br>
-      For instance, any attempt to invoke any primitive other than those listed below
-      will most definitely cause an exception, because in the Q XML-RPC implementation,
-      no provision is made for default handlers.<br>
-      <br>
-      Apart from this, it's possible that calls to known legal methods may trigger an
-      exception. This is not supposed to happen, and the author will be working over
-      time to intercept all such exceptions and wrap them in appropriate response
-      structures. But in the meantime, client app developers should catch any exceptions
-      resulting from their XML-RPC calls and recover appropriately.
-    </blockquote>
-
-    <hr>
-
-    <h3>4.5. Overview of Q XML-RPC Primitives</h3>
-
-    The XML-RPC primitives supported by Q server and client nodes include:
-    <ul>
-      <li><b>i2p.q.ping</b> - test if a server node is alive</li>
-      <li><b>i2p.q.hello</b> - one new server node introduces itself to another server node</li>
-      <li><b>i2p.q.getItem</b> - retrieve an item of content</li>
-      <li><b>i2p.q.putItem</b> - insert an item of content</li>
-      <li><b>i2p.q.getUpdate</b> - retrieve a differential update of peers list (and optionally, catalog update)</li>
-      <li><b>i2.q.search</b> - search a client node for data items matching certain patterns</li>
-    </ul>
-
-    <hr>
-
-    <h3>4.6. i2p.q.ping</h3>
-
-    <blockquote>
-
-      <h4>Overview</h4>
-
-      <blockquote>
-
-        The <b>i2p.q.ping</b> primitive is used to test if a given server or client node
-        is presently online. It can be sent by server nodes, client nodes and client apps.
-
-      </blockquote>
-
-      <h4>Arguments</h4>
-
-      <blockquote>
-        This primitive accepts no arguments, and will fail if any arguments are given.
-      </blockquote>
-
-      <h4>Server Behaviour</h4>
-
-      <blockquote>
-        No action on the part of the receiving server is required, apart from sending back:
-      </blockquote>
-      <table cellspacing=0 cellpadding=4 border=1 width=70% align=center>
-          <tr><td><b>Key</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-          <tr>
-            <td>status</td>
-            <td>string</td>
-            <td>"ok"</td>
-          </tr>
-          <tr>
-            <td>id</td>
-            <td>string</td>
-            <td>The node's nodeId, as a base64 string</td>
-          </tr>
-          <tr>
-            <td>dest</td>
-            <td>string</td>
-            <td>Node's destination, represented as base64 string</td>
-          </tr>
-          <tr>
-            <td>uptime</td>
-            <td>int</td>
-            <td>The number of seconds that this node has been running for</td>
-          </tr>
-          <tr>
-            <td>load</td>
-            <td>float</td>
-            <td>Current load this node is experiencing, as a float between
-              0.0 (no load) to 1.0 (impossibly flatlined)</td>
-          </tr>
-      </table>
-
-      <h4>Client Behaviour</h4>
-
-      <blockquote>
-        Same as server.
-      </blockquote>
-
-    </blockquote>
-
-    <hr>
-
-    <h3>4.7. i2p.q.hello</h3>
-
-    <blockquote>
-
-      <h4>Overview</h4>
-
-      <blockquote>
-
-        The <b>i2p.q.hello</b> primitive is sent by new server nodes to advise other existing
-        server nodes of their existence. It is only sent by server nodes to other server
-        nodes. It is considered an abuse for a client node to send this command.
-
-      </blockquote>
-
-      <h4>Arguments</h4>
-
-      <ul>
-        <li><b>destination</b> (string) - the base64 representation of the calling node's
-          I2P destination (on which the calling node's in-I2P XML-RPC server may be
-          subsequently reached). Same format as the I2P hosts.txt listing.
-      </ul>
-
-      <h4>Server Behaviour</h4>
-
-      <blockquote>
-        If the destination is valid, the receiving server will reply with:
-      </blockquote>
-
-      <table cellspacing=0 cellpadding=4 border=1 width=70% align=center>
-          <tr><td><b>Key</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-          <tr>
-            <td>status</td>
-            <td>string</td>
-            <td>"ok"</td>
-          </tr>
-      </table>
-
-      <blockquote>
-        If the destination is invalid, the receiving server will send back:
-      </blockquote>
-
-      <table cellspacing=0 cellpadding=4 border=1 width=70% align=center>
-          <tr><td><b>Key</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-          <tr>
-            <td>status</td>
-            <td>string</td>
-            <td>"error"</td>
-          </tr>
-          <tr>
-            <td>error</td>
-            <td>string</td>
-            <td>"baddest"</td>
-          </tr>
-      </table>
-
-      <h4>Client Behaviour</h4>
-      
-      <blockquote>
-        <b>i2p.q.hello</b> calls to clients are illegal. Client nodes receiving such
-        calls will respond with:
-      </blockquote>
-
-      <table cellspacing=0 cellpadding=4 border=1 width=70% align=center>
-          <tr><td><b>Key</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-          <tr>
-            <td>status</td>
-            <td>string</td>
-            <td>"error"</td>
-          </tr>
-          <tr>
-            <td>error</td>
-            <td>string</td>
-            <td>"unimplemented"</td>
-          </tr>
-      </table>
-
-    </blockquote>
-
-    <hr>
-
-    <h3>4.8. i2p.q.getItem</h3>
-
-    <blockquote>
-
-      <h4>Overview</h4>
-
-      <blockquote>
-
-        The <b>i2p.q.getItem</b> primitive is used to attempt retrieval of an item of content
-        from a client or server node.
-        
-      </blockquote>
-      
-      <h4>Arguments</h4>
-      
-      <ul>
-        <li><b>key</b> (string) - the base64 key under which the item in question is
-          stored</li>
-      </ul>
-
-      <h4>Server Behaviour</h4>
-
-      <blockquote>
-        Servers receiving this command will only search their own datastore for the item.
-        They will never attempt to on-request this item from other servers.<br>
-        <br>
-        If the server possesses the requested item in its datastore, it will respond with:
-      </blockquote>
-
-      <table cellspacing=0 cellpadding=4 border=1 width=70% align=center>
-          <tr><td><b>Key</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-          <tr>
-            <td>status</td>
-            <td>string</td>
-            <td>"ok"</td>
-          </tr>
-          <tr>
-            <td>metadata</td>
-            <td>struct</td>
-            <td>A nested struct, containing the metadata for the key. (Refer section on
-              metadata).</td>
-          </tr>
-          <tr>
-            <td>data</td>
-            <td>binary data</td>
-            <td>The raw data.</td>
-          </tr>
-      </table>
-
-      <blockquote>
-        If the server doesn't possess the data, it will respond with:
-      </blockquote>
-
-      <table cellspacing=0 cellpadding=4 border=1 width=70% align=center>
-          <tr><td><b>Key</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-          <tr>
-            <td>status</td>
-            <td>string</td>
-            <td>"error"</td>
-          </tr>
-          <tr>
-            <td>error</td>
-            <td>string</td>
-            <td>"notfound"</td>
-          </tr>
-      </table>
-
-
-      <h4>Client Behaviour</h4>
-
-      <blockquote>
-        If the client possesses the key in its own local datastore, it will send back
-        the full data immediately:
-      </blockquote>
-
-      <table cellspacing=0 cellpadding=4 border=1 width=70% align=center>
-          <tr><td><b>Key</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-          <tr>
-            <td>status</td>
-            <td>string</td>
-            <td>"ok"</td>
-          </tr>
-          <tr>
-            <td>metadata</td>
-            <td>struct</td>
-            <td>A nested struct, containing the metadata for the key. (Refer section on
-              metadata).</td>
-          </tr>
-          <tr>
-            <td>data</td>
-            <td>binary data</td>
-            <td>The raw data.</td>
-          </tr>
-      </table>
-
-      <blockquote>
-        If the client doesn't possess the key, it will search its internal catalogues
-        for a server which does have the key.<br>
-        <br>
-        If one or more servers possessing the key are found, the client will on-send
-        an <b>i2p.q.getItem</b> command to each of those servers in turn, until it
-        either successfully retrieves the data, or fails.<br>
-        <br>
-        If the client successfully retrieves the data from one or more of its servers,
-        it will add the data to its internal cache, and reply with the above success
-        response.<br>
-        <br>
-        If the client was unable to source the complete data from any of its servers,
-        it will reply with:
-      </blockquote>
-
-      <table cellspacing=0 cellpadding=4 border=1 width=70% align=center>
-          <tr><td><b>Key</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-          <tr>
-            <td>status</td>
-            <td>string</td>
-            <td>"error"</td>
-          </tr>
-          <tr>
-            <td>error</td>
-            <td>string</td>
-            <td>"notfound"</td>
-          </tr>
-      </table>
-
-    </blockquote>
-
-    <hr>
-
-    <h3>4.9. i2p.q.putItem</h3>
-
-    <blockquote>
-
-      <h4>Overview</h4>
-
-      <blockquote>
-
-        The <b>i2p.q.putItem</b> primitive is used by client nodes to insert a new item
-        of content onto a server node.<br>
-        <br>
-        It is also used by <i>client apps</i> to insert a new item onto their
-        <i>client node</i>.<br>
-        <br>
-        Also, if a server node is receiving a high traffic of requests for a given item,
-        it may at its discretion send <b>i2p.q.putItem</b> commands to peer servers
-        to mirror the item on those servers, and spread the load.
-      </blockquote>
-
-      <h4>Arguments</h4>
-
-      <ul>
-        <li><b>data</b> - (binary) - the raw data to insert. Refer earlier - the compatible
-        Java datatype is <b>byte[]</b>, and Python datatype is <b>xmlrpclib.Binary</b>.</li>
-        <li><b>metadata</b> - (struct) - <b><i>optional</i></b> - a struct of metadata to
-          insert alongside the data. If this is not given, a minimal metadata set will
-          be automatically created by the recipient. See the section on
-          <a href="#metadata">metadata</a>.
-      </ul>
-
-      <h4>Server Behaviour</h4>
-
-      <blockquote>
-        If the server successfully received and stored the data (and optionally provided
-        metadata), it will reply with:
-      </blockquote>
-
-      <table cellspacing=0 cellpadding=4 border=1 width=70% align=center>
-          <tr><td><b>Key</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-          <tr>
-            <td>status</td>
-            <td>string</td>
-            <td>"ok"</td>
-          </tr>
-          <tr>
-            <td>key</td>
-            <td>string</td>
-            <td>The base64 key under which this item has been stored, and which should
-              be used for any subsequent <b>i2p.q.getItem</b> requests for that item
-              within the Q network.</td>
-          </tr>
-      </table>
-
-      <blockquote>
-        However, if the server's datastore is full, the server will not be able to store
-        this item, in which case it will respond with:
-      </blockquote>
-
-      <table cellspacing=0 cellpadding=4 border=1 width=70% align=center>
-          <tr><td><b>Key</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-          <tr>
-            <td>status</td>
-            <td>string</td>
-            <td>"error"</td>
-          </tr>
-          <tr>
-            <td>error</td>
-            <td>string</td>
-            <td>"storefull"</td>
-          </tr>
-      </table>
-
-      <h4>Client Behaviour</h4>
-
-      <blockquote>
-        Client nodes receiving this command will attempt to store the item in their own
-        datastore, and respond immediately with one of the above server responses.<br>
-        <br>
-        In addition, client nodes will enqueue a background job to upload this item to
-        one or more selected server nodes.
-      </blockquote>
-
-    </blockquote>
-
-    <hr>
-
-    <h3>4.10. i2p.q.getUpdate</h3>
-
-    <blockquote>
-
-      <h4>Overview</h4>
-
-      <blockquote>
-
-        The <b>i2p.q.getUpdate</b> primitive is used to request a differential peers list
-        update (which optionally can include a catalog update as well).<br>
-        <br>
-        <i>Client apps</i> invoke this primitive on <i>client nodes</i> to get up-to-date
-        listings of items available in the network. Note that client apps will not
-        hand over any peers list.<br>
-        <br>
-        <i>Client nodes</i> periodically schedule a background job to invoke this primitive
-        on their known servers, such that they keep the most recent possible view of
-        available data and other servers.<br>
-      </blockquote>
-
-      <h4>Arguments</h4>
-
-      <ul>
-        <li><b>since</b> - (int) - unix time in seconds to update from. The recipient
-          will send back a list of all content it has become aware of since this
-          time.</li>
-        <li><b>includePeers</b> - (int) - set to 1 to include peer list update in the return
-          data, 0 to omit.</li>
-        <li><b>includeCatalog</b> - (int) - set to 1 to include catalog update in the return
-          data, 0 to omit.</li>
-      </ul>
-
-      <h4>Server Behaviour</h4>
-
-      <blockquote>
-        On receiving this command, a server node will send back lists of metadata records
-        for all new content (and/or all new peers) it has become aware of since the given
-        date. The full response is formatted as follows:
-      </blockquote>
-
-      <table cellspacing=0 cellpadding=4 border=1 width=70% align=center>
-          <tr><td><b>Key</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-          <tr>
-            <td>status</td>
-            <td>string</td>
-            <td>"ok"</td>
-          </tr>
-          <tr>
-            <td>items</td>
-            <td>list</td>
-            <td>A list of metadata records for new items. Refer to the section on
-              <a href="#metadata">metadata</a> for more information. If the server
-              has not become aware of any new data since the given date (or if the
-              <b>includeCatalog</b> argument was 0), this list will be empty.</td>
-          </tr>
-          <tr>
-            <td>peers</td>
-            <td>list</td>
-            <td>A list of destinations of new peers. If the server has not discovered
-              any new peers since the given date (or if the <b>includePeers</b> argument
-              was 0), this list will be empty.
-          <tr>
-            <td>timeUpdateEnds</td>
-            <td>int</td>
-            <td>unixtime in secs that this update ends. The peer receiving this
-              response should note this time, and quote it as the <b>since</b> argument
-              in the next <b>getUpdate</b> request</td>
-          </tr>
-          <tr>
-            <td>timeNextContact</td>
-            <td>int</td>
-            <td>Advised time (unixtime in sec) for sending the next <b>getUpdate</b> command. The sending
-              peer should not issue any getCatalog commands before this time, but is
-              welcome to issue them after this time. The actual time value is guesstimated
-              by the server node, depending on its current load.</td>
-          </tr>
-      </table>
-
-    <h3>4.11. i2p.q.search</h3>
-
-    <blockquote>
-
-      <h4>Overview</h4>
-
-      <blockquote>
-
-        The <b>i2p.q.search</b> primitive is invoked by client apps to search a client node
-          for data items matching a set of criteria.
-        <br>
-          Only client nodes support this primitive. Server nodes will return an empty
-          result set and an error response.
-      </blockquote>
-
-      <h4>Arguments</h4>
-
-      <ul>
-        <li><b>criteria</b> - (hashtable) - a set of metadata criteria to match. Each key in
-            this hashtable is a metadata key (eg <b>title</b>, <b>type</b> etc), and the
-            corresponding value is a regular expression string to match. Regular expression
-            syntax is documented in the java API in the 
-            <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/regex/Pattern.html">section
-              on class 'Pattern'</a>.<br>
-            <br>
-            The search criteria work 'AND-style', in that if more than one metadata key
-            match pattern is given, then only items matching all of the given criteria
-            will be returned.<br>
-            <br>
-            Python example (using XML-RPC proxy - see code samples below):
-            <blockquote><code><pre>
-result = mynode.i2p.q.search({"type":"text", "summary":"^War.*"})
-metaRecs = result['items']
-</pre></code></blockquote>
-            Java Example (using XML-RPC proxy - see code examples below):
-            <blockquote><code><pre>
-Hashtable criteria = new Hashtable();
-criteria.put("type", "text");
-criteria.put("summary", "^War.*");
-Vector args = new Vector();
-args.addElement(criteria);
-Hashtable result = (Hashtable)mynode.execute("i2p.q.search", args);
-Vector metaRecs = (Vector)result.get("items");
-</pre></code></blockquote>
-            Note that if the <b>criteria</b> argument is empty (no keys/values), then the
-            client node will send back metadata for every item of content it knows of, which
-            (depending on the size of the Q network), could be quite a resource-hungry operation.
-          </li>
-      </ul>
-
-      <h4>Server Behaviour</h4>
-
-        <blockquote>
-          Servers receiving this command will send back an error response:
-        </blockquote>
-        
-        <table cellspacing=0 cellpadding=4 border=1 width=70% align=center>
-            <tr><td><b>Key</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-            <tr>
-              <td>status</td>
-              <td>string</td>
-              <td>"error"</td>
-            </tr>
-            <tr>
-              <td>error</td>
-              <td>string</td>
-              <td>"unimplemented"</td>
-            </tr>
-        </table>
-        
-      <h4>Client Behaviour</h4>
-
-      <blockquote>
-          Client nodes receiving this command will send back the following response:
-      </blockquote>
-
-        <table cellspacing=0 cellpadding=4 border=1 width=70% align=center>
-            <tr><td><b>Key</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-            <tr>
-              <td>status</td>
-              <td>string</td>
-              <td>"ok"</td>
-            </tr>
-            <tr>
-              <td>items</td>
-              <td>vector</td>
-              <td>A list of metadata records (Hashtables) for items which match the given
-                search criteria, and are retrievable through this client
-                node (ie, the client node either possesses the item, or knows one or more
-                servers which possess the item).<br>
-            </tr>
-        </table>
-
-    </blockquote>
-
-
-    <hr>
-
-    <h2>5. Client Program Examples</h2>
-
-    <h3>5.1. Overview</h3>
-
-    This section provides a couple of simple examples of client app programming.<br>
-    <br>
-    At present, only Python and Java examples are given.<br>
-    <br>
-    (If you don't know either of these languages, you should be
-    able to get the general drift by studying the examples, sufficient to map the concepts to the
-    XML-RPC API available to your preferred language.)<br>
-    <br>
-    The examples below communicate with a <i>client node</i> XML-RPC server (running on the
-    local machine and listening on its default port of 7651), and perform simple
-    operations of data insertion, catalog fetching and data retrieval.
-
-    <hr>
-
-    <h3>5.2. Java Example</h3>
-
-    To run this example, you'll need:
-    <ul>
-      <li>A running I2P installation, with an instance of a Q client node.
-      <li>The I2P standard jarfiles declared in your java <b>CLASSPATH</b></li>
-      <li>The standard Apache XML-RPC library jarfile in your <b>CLASSPATH</b> (which you will
-        already have on your CLASSPATH, because this is part of installing Q). Recall that you
-        can get a copy of Apache java XML-RPC lib jarfile from
-        <a href="http://ws.apache.org/xmlrpc">http://ws.apache.org/xmlrpc</a>).</li>
-    </ul>
-    Now for the code (heavily annotated, so you don't necessarily need to know or understand Java), which
-    should be written to a source file called <b>QDemo.java</b>. Note that this client would be a
-    significantly shorter if it instantiated a <b>QClientNode</b> class directly and invoked its methods,
-    but that is not what we're showing here - we're demonstrating the use of the client node's XML-RPC
-    interface.
-    <blockquote>
-      <code><pre>
-// QDemo.java
-//
-// A simple demo example of a Q client application, which 
-// communicates with a running Q client node on the local
-// machine via its TCP XML-RPC interface
-//
-// If your client node is not running on localhost, or
-// if it's listening on a port other than the default
-// 7651, you'll need to change the code below.
-//
-// Note that this demo is bloated by the fact we're using
-// raw XML-RPC.
-// 
-// The following exercises are left to the reader:
-//  1. Modify this app so that instead of using the XML-RPC
-//     interface, it instantiates a QClientNode, and
-//     invokes its methods directly.
-//  2. Write a thin wrapper class which instantiates an XML-RPC
-//     client, and offers simpler access methods (thus avoiding
-//     the need to create and populate Vectors of args before
-//     calling, and pick through a reply Hashtable after the call),
-//     and create a version of this demo which uses the wrapper.
-
-// pull in some standard java stuff
-import java.*;
-import java.lang.*;
-import java.util.*;
-import java.net.*;
-import java.io.*;
-
-// pull in some xml-rpc stuff
-import org.apache.xmlrpc.*;
-
-// since we're talking to the node via xmlrpc, and talking to
-// it in a separate VM, we don't need to import any Q packages
-
-// Define a minimal demo class, which kust defines a
-// main method enabling us to run the demo from a shell.
-//
-// For the purposes of this demo, we're assuming that your Q client node is
-// running on your local machine, and that you haven't altered the
-// listening port (default 7651) for the client's XML-RPC interface.
-
-public class QDemo {
-
-    // just define a main so we can run this from a shell
-    static public void main(String [] args)
-        throws MalformedURLException, XmlRpcException, IOException
-    {
-        // for getting and analysing replies from node
-        Hashtable result;
-        String status;
-
-        // Create a new client app object
-        XmlRpcClient myClient = new XmlRpcClient("http://127.0.0.1:7651");
-
-        // -------------------------------------
-        // First action - execute a 'ping' on this peer
-        // -------------------------------------
-
-        Vector noArgs = new Vector();
-        result = (Hashtable)myClient.execute("i2p.q.ping", noArgs);
-        print("ping: result=" + result);
-
-        // -------------------------------------
-        // Second action - insert an item of data
-        // -------------------------------------
-
-        // mark the current time, we'll use this later
-        Integer then = new Integer((int)(new Date().getTime() / 1000));
-
-        // create metadata
-        // (note from previous chapter that metadata is optional)
-        Hashtable meta = new Hashtable();
-        meta.put("type", "text");
-        meta.put("abstract", "a simple piece of demo data");
-        meta.put("mimetype", "text/plain");
-
-        // create some data
-        String data = "Hello, world";
-
-        // set up the arguments list
-        Vector insertArgs = new Vector();
-        insertArgs.addElement(meta);
-        insertArgs.addElement(data.getBytes()); // must insert data as byte[]
-            
-        // and do the insert
-        result = (Hashtable)myClient.execute("i2p.q.putItem", insertArgs);
-        print("putItem: result=" + result);
-
-        // check what happened
-        status = (String)result.get("status");
-        String key;
-        if (status.equals("ok")) {
-            // insert succeeded
-            key = (String)result.get("key");
-            print("Insert successful");
-        } else {
-            // insert failed, bail
-            print("Insert failed: error=" + (String)result.get("error"));
-            return;
-        }
-
-        // -------------------------------------
-        // Third action - check for catalog updates
-        // (which should include what we've just inserted)
-        // -------------------------------------
-
-        // create an args list, with just the date we noted before the insert
-        Vector updateArgs = new Vector();
-        updateArgs.addElement(then);
-        // add the flags
-        updateArgs.addElement(new Integer(0));   // 'includePeers'
-        updateArgs.addElement(new Integer(1));   // 'includeCatalog'
-
-        // execute the 'getCatalog'
-        result = (Hashtable)myClient.execute("i2p.q.getUpdate", updateArgs);
-        print("getUpdate: result="+result);
-
-        // pick out the results, and search for what we just inserted
-        int i;
-        Vector items = (Vector)result.get("items");
-        int nitems = items.size();
-        boolean foundit = false;
-        for (i = 0; i < nitems; i++) {
-            // get the nth item
-            Hashtable metaRec = (Hashtable)items.get(i);
-            String thisKey = (String)metaRec.get("key");
-            if (thisKey.equals(key)) {
-                // yay, got it!
-                foundit = true;
-                break;
-            }
-        }
-
-        // did we get it?
-        if (!foundit) {
-            print("wtf? we inserted it but it's not in the catalog!");
-            return;
-        }
-
-        // yep, we got it, so try to retrieve it back
-        Vector getArgs = new Vector();
-        getArgs.addElement(key);
-        result = (Hashtable)myClient.execute("i2p.q.getItem", getArgs);
-        print("getItem: result=" + result);
-
-        // did we get it?
-        status = (String)result.get("status");
-        if (!status.equals("ok")) {
-            print("getItem failed: " + (String)result.get("error"));
-            return;
-        }
-
-        // yep, got it
-        byte [] binData = (byte [])result.get("data");
-        String strData = new String(binData);
-        print("getItem: success, data='"+strData+"'");
-
-        print("--- END OF Q CLIENT DEMO ---");
-    }
-
-    // a convenient shorthand method for printing stuff to stdout
-    static void print(String msg) {
-        System.out.println(msg);
-    }
-}
-        </pre></code>
-      </blockquote>
-
-    <hr>
-
-    <h3>5.3. Python Example</h3>
-
-    To run this example, you will need a running I2P installation, including a running instance
-    of a Q client node.<br>
-    <br>
-    Note that, in contrast to Java, Python 2.3 and later have all the necessary XML-RPC libraries built in.
-    <br>
-    Now for some code (again, heavily annotated). This, together with the previous example, present an
-    interesting comparison between some of Java and Python's ways of doing things.
-    <blockquote>
-      <code><pre>
-#!/usr/bin/env python
-"""
-QDemo.py
-
-A simple demo example of a Q client application, which 
-communicates with a running Q client node on the local
-machine via its TCP XML-RPC interface
-
-If your client node is not running on localhost, or
-if it's listening on a port other than the default
-7651, you'll need to change the code below.
-
-Note that this demo is bloated by the fact we're using
-raw XML-RPC.
-
-The following exercise is left to the reader:
- * Write a thin wrapper class which instantiates an XML-RPC
-   client, and offers simpler access methods (thus avoiding
-   the need to pick through a reply dict after the call),
-   and create a version of this demo which uses the wrapper.
-"""
-
-# a coupla needed imports
-from time import time
-from xmlrpclib import ServerProxy, Binary
-
-# For the purposes of this demo, we're assuming that your Q client node is
-# running on your local machine, and that you haven't altered the
-# listening port (default 7651) for the client's XML-RPC interface.
-
-def qdemo():
-    # Create a new client app object
-    myClient = ServerProxy("http://127.0.0.1:7651")
-
-    # -------------------------------------
-    # First action - execute a 'ping' on this peer
-    # -------------------------------------
-
-    result = myClient.i2p.q.ping()
-    print "ping: result=%s" % result
-
-    # -------------------------------------
-    # Second action - insert an item of data
-    # -------------------------------------
-
-    # mark the current time, we'll use this later
-    then = int(time())
-
-    # create metadata
-    # (note from previous chapter that metadata is optional)
-    meta = {
-        "type" : "text",
-        "abstract" : "a simple piece of demo data",
-        "mimetype" : "text/plain",
-        }
-
-    # create some data, and binary-wrap it
-    data = "Hello, world"
-    binData = Binary(data)
-
-    # and do the insert
-    result = myClient.i2p.q.putItem(meta, binData)
-    print "putItem: result=%s" % result
-
-    # check what happened
-    if result["status"] == "ok":
-        # insert succeeded
-        key = result["key"]
-        print "Insert successful"
-    else:
-        # insert failed, bail
-        print "Insert failed: error=%s" % result['error']
-        return;
-
-    # -------------------------------------
-    # Third action - check for catalog updates
-    # (which should include what we've just inserted)
-    # -------------------------------------
-
-    # execute the 'getUpdate'
-    result = myClient.i2p.q.getUpdate(then, 0, 1)
-    print "getUpdate: result=%s" % result
-
-    # pick out the results, and search for what we just inserted
-    foundit = False
-    for metaRec in result['items']:
-        if metaRec['key'] == key:
-            # yay, got it!
-            foundit = True
-            break
-
-    # did we get it?
-    if not foundit:
-        print "wtf? we inserted it but it's not in the catalog!"
-        return;
-
-    # yep, we got it, so try to retrieve it back
-    print "getCatalog: found the item we just inserted"
-    result = myClient.i2p.q.getItem(key)
-    print "getItem: result=%s" % result
-
-    # did we get it?
-    if result["status"] != "ok":
-        print "getItem failed: %s" + result["error"]
-        return;
-
-    # yep, got it (note that data is an xmlrpclib.Binary object,
-    # and the raw data we want is in its .data attribute)
-    print "getItem: success, data='%s'" % result['data'].data
-
-    print "--- END OF Q CLIENT DEMO ---"
-
-# run the demo func if this script is executed directly
-if __name__ == '__main__':
-    qdemo()
-        </pre></code>
-      </blockquote>
-
-    <a name="metadata"></a>
-
-    <hr>
-
-    <h2>6. Keys and Metadata</h2>
-
-    <h3>6.1. Overview</h3>
-    Like Freenet, content is stored in Q as (data, metadata) pairs.<br>
-    <br>
-    However, there's a difference. On Freenet, metadata is stored as a string of up to
-    32k length, and must be parsed (and sometimes executed) by client code. On the other
-    hand, metadata is exposed in Q as an XML-RPC <b>struct</b> (Java <b>Hashtable</b> or
-    <b>Properties</b> object, or Python <b>dict</b>, or Perl <b>associative array</b> etc).<br>
-    <br>
-    If a content item gets inserted to the Q network without metadata, a minimal metadata set
-    will be transparently generated, and is guaranteed to contain at least the following
-    elements:<br>
-    <br>
-    <table cellspacing=0 cellpadding=4 border=1 width=70% align=center>
-        <tr><td><b>Key</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-        <tr>
-          <td>size</td>
-          <td>int</td>
-          <td>Size of the stored data item, in bytes</td>
-        </tr>
-        <tr>
-          <td>dataHash</td>
-          <td>string</td>
-          <td>a base64 representation of the SHA256 hash of the full raw data, using the I2P
-            base64 alphabet</td>
-        </tr> 
-    </table>
-    <br>
-
-    <hr>
-
-    <h3>6.2. Node IDs</h3>
-    
-    When Q nodes are first created, they generate themselves a random
-    I2P privKey/dest keypair using the in-I2P services.<br>
-    <br>
-    The I2P destination gets converted to what we call a <b>Q Node ID</b>, as follows:
-    <ul>
-      <li>Start with binary destination (not base64)</li>
-      <li>Determine the SHA256 binary digest of this dest</li>
-      <li>Encode the resulting binary string via I2P's base64 alphabet</li>
-    </ul>
-
-    <hr>
-
-    <h3>6.3. Keys</h3>
-
-    Here, 'key' means the unique short string, by which items of content can be
-    retrieved, and which is returned from an i2p.q.putItem command.<br>
-    <br>
-    Like Freenet's <b>CHK@</b> keytype, Q keys are hashes of the key's content and
-    metadata.<br>
-    <br>
-    The recipe for calculating the 'key' of a particular item of metadata+data is:
-    <ol>
-      <li>If no metadata is submitted with the data, create a minimal metadata as per above</li>
-      <li>Serialise out the metadata into a string representation, with the fieldnames in
-        alphanumeric order. The format of such string is one line per metadata field/value
-        pair per line, in the format:
-        <blockquote><code>
-            metadatakeyname=metadatakeyvalue\n
-          </code></blockquote>
-      </li>
-      <li>Calculate the binary SHA1 digest of this serialised metadata string</li>
-      <li>Base64-encode this binary digest via the I2P Base64 alphabet</li>
-    </ol>
-
-    <hr>
-
-    <h3>6.4. Q Metadata Conventions</h3>
-
-    Additional to the core metadata defined above, there is a convention in Q that the
-    following optional extra metadata
-    keys be provided on insert, and recognised and honoured on retrieve.<br>
-    <br>
-    It is highly recommended that these keys be included
-    in metadata when content is inserted:<br>
-    <br>
-    <table cellspacing=0 cellpadding=4 border=1 width=70% align=center>
-        <tr><td><b>Key</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-        <tr>
-          <td>title</td>
-          <td>string</td>
-          <td>A short and descriptive title for the item, preferably formatted as
-            a filename which is legal and convenient on all main operating systems, ie,
-            containing only alphanumerics, '-', '_' and '.'.<br>
-            <br>
-            It is highly advisable that an appropriate file extension appear at the
-            end of the title. Refer to the <a href="#security">Security Considerations</a>
-            section below.
-            <br>
-            It is expected that client applications will use this title field when
-            displaying available content lists to users.
-          </td>
-        </tr> 
-        <tr>
-          <td>type</td>
-          <td>string</td>
-          <td>Generic type of material, using the following superset of the  eMule/Donkey
-            classifications:
-            <ul>
-              <li>text</li>
-              <li>html</li>
-              <li>image</li>
-              <li>audio</li>
-              <li>video</li>
-              <li>software</li>
-              <li>archive</li>
-              <li>misc</li>
-            </ul>
-          </td>
-        </tr>
-        <tr>
-          <td>mimetype</td>
-          <td>string</td>
-          <td>A recognised mime-type, as per RFC1341, RFC1521, RFC1522, such as
-            <b>audio/mpeg</b>, <b>text/plain</b> etc.<br>
-            <br>
-            This will help client app developers devise ways of disposing with data items
-            they request from client nodes.<br>
-            <br>
-            For instance, client apps with http front ends
-            may send back this mimetype as the value of the <b>Content-type:</b> header,
-            (and possibly take preventative action with potentially hazardous mimetypes, such
-            as those which some browsers such as IE might trust and execute blindly as
-            binary code).<br>
-            <br>
-            Alternatively, gui-based or cli-based client apps may convert this mimetype to
-            an appropriate benign file extension (such as <b>.txt</b>,
-            <b>.ogg</b>, <b>.jpg</b> etc). See <a href="#security">Security
-              Considerations</a> below.
-          </td>
-        </tr> 
-        <tr>
-          <td>keywords</td>
-          <td>string</td>
-          <td>A set of space-separated keywords describing this item, intended for
-            human reading, as well as automatic parsing by client apps.</td>
-        </tr>
-        <tr>
-          <td>abstract</td>
-          <td>string</td>
-          <td>A short descriptive summary of the nature of the data, intended for
-            human reading, as well as automatic pattern matching searches by client
-            apps.</td>
-        </tr>
-    </table>
-        <br>
-        <br>
-
-    <hr>
-
-    <h3>6.5. One Data Item, Many Metadata Sets?</h3>
-
-    It is perfectly possible, and legal, for one item of data to be referenced by two
-    completely different items of metadata.<br>
-    <br>
-    Since content <b>keys</b> are a hash of metadata, which in turn contains a hash of the data,
-    then two pieces of metadata referencing the same data item, but containing different
-    metadata values, will end up with different keys.<br>
-    <br>
-    So as far as key addresses go, there will be a many-to-1 relationship between raw
-    content keys, and the data returned under these keys.<br>
-    <br>
-   
-    <a name="security">
-
-    <hr>
-
-    <h2>7. Security Considerations</h2>
-
-    All Peer2Peer software (as with all networked software in general) carries with it a set of
-    devastating security risks which should be respected to the utmost.<br>
-    <br>
-    This applies in no small part to Q.<br>
-    <br>
-    So this brief sermon is addressed to anyone writing any client applications or
-    APIs talking to the Q network.<br>
-    <br>
-    <b>Any</b> material which involves the execution of code on a client machine is risky.
-    However, much of the risk can be managed if the code is open source and peer-reviewed.<br>
-    <br>
-    Perhaps the biggest issue as far as Q is concerned is this:
-
-    <blockquote><b>
-        Client app developers should never, <b><i>NEVER</i></b> implicitly
-        trust incoming content, and should always assume that malicious remote users
-        <b>will</b> insert content which attempts to compromise other users' systems.
-      </b></blockquote>
-
-    If a Q client app wants to offer filetype-specific support, then perhaps a good
-    strategy is for the client app to use a <b>whitelist</b> of
-    known low-risk file extensions, such as <b>.txt</b>, or (possibly)
-    <b>.ogg</b>, <b>.png</b> etc. Recall that in some Windows configurations, even
-    <b>.jpg</b> can carry an arbitrary code execution attack!<br>
-    <br>
-    Note that <b>.html</b> (<b>text/html</b>) is especially dangerous, and
-    should be respected accordingly.<br>
-    <br>
-    Support for <b>.html</b> could be a real boon. For instance, it could allow
-    I2P users to publish an I2P equivalent of freenet's <i>freesites</i> - static
-    HTML websites which are accessible even when the author goes offline.<br>
-    <br>
-    However, if a client app chooses to recognise <b>.html</b>, it should either
-    use a code-screening mechanism like freenet's <b>fproxy</b> and keep it
-    up to date with all the latest advisories, or use a mandatory-proxy
-    mechanism like I2P's <b>eepProxy</b>.<br>
-    <br>
-    One possiblility is to serve up such content via a totally in-I2P http interface,
-    such that Joe can view the content via his regular eeproxy-configured browser.<br>
-    <br>
-    This is a typical case where security and ease/convenience can end up in
-    direct conflict. Automatic handling of content according to data type
-    is great from a Joe Sixpack Windows User point of view, but it is a snake-pit
-    of risks that can potentially result in any of the following (or worse):
-    <ul>
-      <li>Set up Joe's computer as a spambot</li>
-      <li>Get Joe's personal credit card and other info, and use this criminally</li>
-      <li>Download child pornography or terrorist information onto Joe's PC, use an
-        exploit to get Joe's IP address and/or identity details, and report this to
-        authorities, thus framing Joe and sending him off undeservedly to Club Fed or 
-        Her Majesty's</li>
-      <li>Mount a DDoS, anonymity or other attack on the I2P network</li>
-      <li>Further spread additional content for achieving more of the above on 
-        other unsuspecting users.</li>
-    </ul>
-
-    The crux of this lecture is that client app writers have a huge responsibility to
-    ensure their apps are safe against malicious content.<br>
-    <br>
-    Perhaps the best and most
-    practical solution is to just store downloaded material into a directory
-    known to and owned by the user, and make it the user's task and responsibility to
-    manually copy materials out of this directory and take responsibility for how s/he uses
-    this content thereafter.<br>
-    <br>
-
-      <a name="contact"></a>
-
-    <hr>
-
-    <h2>8. Contacting the Author</h2>
-
-    I am <b>aum</b>, and can be reached as <b>aum</b> on in-I2P IRC networks, and also
-    at the in-I2P email address of <b>aum@mail.i2p</b>.<br>
-    <br>
-
-    <hr>
-    <center>
-      <small>
-        <a href="#intro">Introduction</a> |
-        <a href="#xmlrpc">XML-RPC</a> |
-        <a href="#arch">Architecture</a> |
-        <a href="#commands">Commands</a> |
-        <a href="#examples">Example&nbsp;Code</a> |
-        <a href="#metadata">Metadata</a> |
-        <a href="#security">Security</a> |
-        <a href="#contact">Contact us</a>
-      </small>
-    </center>
-    <hr>
-    <br>
-
-    <!-- Created: Sat Mar 26 11:22:24 NZST 2005 -->
-    <!-- hhmts start -->
-Last modified: Sat Apr  2 13:31:08 NZST 2005
-<!-- hhmts end -->
-  </body>
-</html>
diff --git a/apps/q/java/build.xml b/apps/q/java/build.xml
deleted file mode 100644
index 8fed7c7a2a2509c3c5d80e366dd9d70e7ffe97b1..0000000000000000000000000000000000000000
--- a/apps/q/java/build.xml
+++ /dev/null
@@ -1,90 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project basedir="." default="all" name="aum">
-  <!-- Written to assume that classpath is rooted in the current directory. -->
-  <!-- So this should be OK if you make this script in the root of a filesystem. -->
-  <!-- If not, just change src.dir to be the root of your sources' package tree -->
-  <!-- and use e.g. View over a Filesystem to mount that subdirectory with all capabilities. -->
-  <!-- The idea is that both Ant and NetBeans have to know what the package root is -->
-  <!-- for the classes in your application. -->
-
-  <!-- Don't worry if you don't know the Ant syntax completely or need help on some tasks! -->
-  <!-- The standard Ant documentation can be downloaded from AutoUpdate and -->
-  <!-- and then you can access the Ant manual in the online help. -->
-
-  <target name="init">
-    <property location="build" name="classes.dir"/>
-    <property location="src" name="src.dir"/>
-    <property location="doc/q/api" name="javadoc.dir"/>
-    <property name="project.name" value="${ant.project.name}"/>
-    <property location="${project.name}.jar" name="jar"/>
-    <property location="q.war" name="war"/>
-  </target>
-
-  <target name="builddep">
-    <ant dir="../../i2ptunnel/java/" target="build" />
-    <!-- i2ptunnel builds ministreaming and core -->
-  </target>
-  
-  <target depends="init,builddep" name="compile">
-    <!-- Both srcdir and destdir should be package roots. -->
-    <mkdir dir="${classes.dir}"/>
-    <javac debug="true" 
-           deprecation="true" 
-           destdir="${classes.dir}" 
-           srcdir="${src.dir}"
-           classpath="xmlrpc.jar:../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar:../../i2ptunnel/java/build/i2ptunnel.jar" >
-      <!-- To add something to the classpath: -->
-      <!-- <classpath><pathelement location="${mylib}"/></classpath> -->
-      <!-- To exclude some files: -->
-      <!-- <exclude name="com/foo/SomeFile.java"/><exclude name="com/foo/somepackage/"/> -->
-    </javac>
-  </target>
-
-  <target depends="init,compile" name="jar">
-    <!-- To make a standalone app, insert into <jar>: -->
-    <!-- <manifest><attribute name="Main-Class" value="com.foo.Main"/></manifest> -->
-    <!-- <jar basedir="${classes.dir}" compress="true" jarfile="${jar}"> -->
-    <jar compress="true" jarfile="${jar}">
-      <!-- <jar basedir="." compress="true" jarfile="${jar}" includes="**/*.class,doc/**/*.html"> -->
-      <fileset dir="${classes.dir}"/>
-      <fileset dir="." includes="qresources/**"/>
-      <manifest>
-        <attribute name="Main-Class" value="net.i2p.aum.q.QMgr"/>
-        <attribute name="Class-Path" value="i2p.jar xmlrpc.jar mstreaming.jar streaming.jar jbigi.jar"/>
-      </manifest>
-    </jar>
-  </target>
-
-    <target depends="init,compile" name="war">
-        <!-- To make a standalone app, insert into <jar>: -->
-        <!-- <manifest><attribute name="Main-Class" value="com.foo.Main"/></manifest> -->
-        <war compress="true" jarfile="${war}" webxml="web.xml">
-          <!-- <fileset file="build/net/i2p/aum/q/QConsole.class"/> -->
-          <classes dir="build" includes="**/QConsole.class"/>
-          <classes dir="build" includes="**/HTML/**"/>
-          <!-- <fileset includes="**/HTML/*.class"/> -->
-          <lib file="xmlrpc.jar"/>
-        </war>
-    </target>
-
-  <target depends="init,jar,war" description="Build everything." name="all"/>
-
-  <target depends="init" description="Javadoc for my API." name="javadoc">
-    <mkdir dir="${javadoc.dir}"/>
-    <javadoc destdir="${javadoc.dir}" packagenames="*">
-      <sourcepath>
-        <pathelement location="${src.dir}"/>
-      </sourcepath>
-      <sourcepath>
-        <pathelement location="/java/xmlrpc-1.2-b1/src/java"/>
-      </sourcepath>
-    </javadoc>
-  </target>
-
-  <target depends="init" description="Clean all build products." name="clean">
-    <delete dir="${classes.dir}"/>
-    <delete dir="${javadoc.dir}"/>
-    <delete file="${jar}"/>
-  </target>
-
-</project>
diff --git a/apps/q/java/qresources/html/404.html b/apps/q/java/qresources/html/404.html
deleted file mode 100644
index c25e9661caa13eb669d8303702c1b9f607be833b..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/404.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<table align="center" class="mainpaneitem">
-  <tr>
-    <td class="formHeading">Item Not Found</td>
-  </tr>
-  <tr>
-    <td align=center>Failed to retrieve item:<br>
-      <code><tmpl_var 404_uri></code>
-    </td>
-  </tr>
-</table>
diff --git a/apps/q/java/qresources/html/about.html b/apps/q/java/qresources/html/about.html
deleted file mode 100644
index 39e0b552352ea537536e951476344fa2113ed672..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/about.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<TABLE align="center" class="mainpaneitem">
-  <TR>
-    <TD class="formHeading">About Q</TD>
-  </tr>
-  <tr class="formBody">
-    <TD align=center>
-      <p>Version
-<tmpl_if version>
-      <tmpl_var version></p>
-<tmpl_else>
-      0.0.1
-</tmpl_if>
-      <p>Designed and engineered by <a href="mailto:aum@mail.i2p">aum</a></p>
-
-      <p>Copyright &copy; 2005 by aum.
-      <br>Released under the
-      <br>GNU Lesser General Public License</p>
-        
-      <p style="font-size: smaller; font-style: italic">
-         Many thanks to jrandom, smeghead and frosk<br>
-         for their patient and knowledgeable support<br>
-         in helping this python programmer<br>
-         get partly proficient in java.</p>
-    </TD>
-  </TR>
-</TABLE>
-
diff --git a/apps/q/java/qresources/html/addrefform.html b/apps/q/java/qresources/html/addrefform.html
deleted file mode 100644
index 02a96a4adc4e1a52d247d714ac2a27539aca5265..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/addrefform.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<table align="center" class="mainpaneitem" width=80%>
-  <tr>
-    <td class="formHeading">Add Hub Noderef</td>
-  </tr>
-
-  <form action="/tools" method="POST" class="formBody">
-
-  <input type="hidden" name="cmd" value="addref">
-
-  <tr>
-    <td align="center">
-      <table width=70%>
-        <tr>
-          <td>
-            In order for your node to join a Q network, it must have a ref to at
-            least one of the running Q Hubs. You need to get a Q Hub noderef and
-            paste it here.
-          </td>
-        </tr>
-        
-        <tr>
-          <td align=center>
-            <textarea name="noderef" cols=40 rows=6></textarea>
-          </td>
-        </tr>
-        
-        <tr>
-          <td align="center">
-            <input type="submit" name="submit" value="Add Hub Noderef!">
-          </td>
-        </tr>
-
-      </table>
-    </td>
-  </tr>
-  
-  </form>
-
-</table>
-
diff --git a/apps/q/java/qresources/html/aiealert.html b/apps/q/java/qresources/html/aiealert.html
deleted file mode 100644
index 01ddecbd50f4c3e8470a8cba1f3700c85e0f66ef..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/aiealert.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<table align="center" class="mainpaneitem"  width=80%>
-  <tr>
-    <td class="formHeading" colspan=2>Critical Anonymity Alert!</td>
-  </tr>
-  <tr>
-    <td>
-      <table cellspacing=0 cellpadding=5 align=center border=1>
-
-        <tr>
-          <td>
-            <p>You are attempting to view a QSite via the Internet Explorer web browser.
-              </p>
-            <p>We have blocked the connection to protect your anonymity.
-              </p>
-            <p>As a matter of policy, Q does not support QSite browsing via Microsoft
-               Internet Explorer (MSIE), because of that browser's abysmal track record with
-               regard to security. If we did allow you to browse Q via MSIE, it would
-               be easy for a malicious QSite author to embed hostile content which 
-               undermines your computer's security and compromises your anonymity.
-               </p>
-             <p>If you want to surf I2P QSites, you'll need to use a more secure web
-                browser such as <a href="http://www.mozilla.org">Mozilla or Firefox</a>.
-          </td>
-        </tr>
-
-      </table>
-    </td>
-  </tr>
-</table>
diff --git a/apps/q/java/qresources/html/anonalert.html b/apps/q/java/qresources/html/anonalert.html
deleted file mode 100644
index 48a980d7fb7418f7acfee7eaf3618400e6d74df8..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/anonalert.html
+++ /dev/null
@@ -1,52 +0,0 @@
-<table align="center" class="mainpaneitem"  width=80%>
-  <tr>
-    <td class="formHeading" colspan=2>Critical Anonymity Alert!</td>
-  </tr>
-  <tr>
-    <td>
-      <table cellspacing=0 cellpadding=5 align=center border=1>
-
-        <tr>
-          <td>
-            <p>You are attempting to view a QSite via an unprotected link.
-              </p>
-            <p>We have blocked the connection to protect your anonymity. If we don't
-              do this, then a malicious QSite author can insert content into the QSite
-              which triggers oubound hits to arbitrary servers on the mainstream web,
-              which in turn can easily reveal a lot of personal information about you
-              and the QSite you are accessing.
-              </p>
-            <p>If you want to browse QSites with your web browser with greater safety,
-              you'll have to follow these simple steps:
-              </p>
-              <ol>
-                <li>Edit your I2P <code>hosts.txt</code> file and add the following entry
-                  (all on one line):
-                  <blockquote><code><textarea rows=5 cols=50>q.i2p=<tmpl_var dest></textarea></code><blockquote>
-                  (and make sure you do NOT reveal this entry to anyone else)
-                  </li>
-                <li>Configure one of your web browsers to use the proxy server:
-                  <blockquote><code>localhost:4444</code></blockquote>
-                  (or whatever address you have configured your I2P EEProxy to listen on,
-                  if you've changed it)<br><br>
-                  </li>
-                <li>Start up the browser you have just configured, and enter the web address:
-                  <blockquote><code>http://q.i2p</code></blockquote>
-                  </li>
-              </ol>
-            <p>Even if you do this, you still won't have a 100% guarantee of anonymity, since
-               a malicious QSite author can send code to your browser which exploits a vulnerability
-               in the browser to compromise your anonymity (eg, accessing third party cookies, executing
-               arbitrary code, accessing your local filesystem, uploading compromising information
-               about you to hostile I2P EEPsites etc). But you can relax (somewhat) in the knowledge
-               that such attacks are much more difficult, particularly if you use a decent web browser.
-               </p>
-            <p>Thank you for your co-operation. We wish you happy and safe browsing.
-               </p>
-          </td>
-        </tr>
-
-      </table>
-    </td>
-  </tr>
-</table>
diff --git a/apps/q/java/qresources/html/downloads.html b/apps/q/java/qresources/html/downloads.html
deleted file mode 100644
index f30ed5d1ef7a60dd6d4b2fa3c92c74f56ced325b..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/downloads.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<table align="center" class="mainpaneitem">
-  <tr>
-    <td class="formHeading">Q Downloads</td>
-  </tr>
-  <tr>
-    <td>Not implemented yet</td>
-  </tr>
-</table>
-
diff --git a/apps/q/java/qresources/html/genkeysform.html b/apps/q/java/qresources/html/genkeysform.html
deleted file mode 100644
index 3ffaa1f1f9b340356a1fe847e01a0d71409eca65..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/genkeysform.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<table align="center" class="mainpaneitem">
-  <tr>
-    <td class="formHeading">Generate New Keypair</td>
-  </tr>
-  <tr>
-    <td align="center">
-      <table>
-        <tr>
-          <td>
-            Click the button to generate a new keypair for
-            future inserts of signed space keys.
-          </td>
-        </tr>
-      </table>
-    </td>
-  </tr>
-  
-  <tr>
-    <td align="center">
-      <form action="/tools" method="POST" class="formBody">
-        <input type="hidden" name="cmd" value="genkeys">
-        <input type="submit" name="submit" value="Generate Keys!">
-      </form>
-    </td>
-  </tr>
-
-</table>
-
diff --git a/apps/q/java/qresources/html/genkeysresult.html b/apps/q/java/qresources/html/genkeysresult.html
deleted file mode 100644
index c693d306fb9d86adb6925f434722912f4b3c1410..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/genkeysresult.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<table align="center" class="mainpaneitem">
-  <tr>
-    <td colspan=2 class="formHeading">Your New Keys</td>
-  </tr>
-
-  <tr>
-    <td align="center" colspan=2>
-      <table>
-        <tr>
-          <td>
-            Please save these keys in a safe place,
-            preferably on an encrypted partition:
-          </td>
-        </tr>
-      </table>
-    </td>
-  </tr>
-  
-  <tr>
-    <td>Public Key: </td>
-    <td>
-      <input readonly type="text" size=32 style="font-family: courier, monospace" value="<tmpl_var publickey>">
-    </td>
-  </tr>
-
-  <tr>
-    <td>Private Key: </td>
-    <td><input readonly type="text" size=32 style="font-family: courier, monospace" value="<tmpl_var privatekey>"></td>
-  </tr>
-
-</table>
-
diff --git a/apps/q/java/qresources/html/getform.html b/apps/q/java/qresources/html/getform.html
deleted file mode 100644
index 313d7cdc6450efcc9580201078168a0066f3358d..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/getform.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<table align="center" class="mainpaneitem" border=0>
-  <TR>
-    <TD class="formHeading" colspan="3">Retrieve Content</TD>
-  </tr> 
-  <tr class="formBody">
-    <form action="/home" method="POST">
-      <input type="hidden" name="cmd" value="get">
-
-      <TD>URI:</TD>
-      <td><INPUT type="text" name="uri" value="Q:" size="60" maxlength="128"></td>
-      <td><INPUT type="submit" name="submit" value="Retrieve it"></td>
-
-    </form>
-  </TR>
-</table>
diff --git a/apps/q/java/qresources/html/help.html b/apps/q/java/qresources/html/help.html
deleted file mode 100644
index b50fbcc817e568fe059ace4c047ed01944fe86eb..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/help.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<table align="center" class="mainpaneitem">
-  <tr>
-    <td class="formHeading">Q Help</td>
-  </tr>
-  <tr>
-    <td>
-      Not written yet
-    </td>
-  </tr>
-</table>
-
diff --git a/apps/q/java/qresources/html/jobs.html b/apps/q/java/qresources/html/jobs.html
deleted file mode 100644
index 08191f9310debf8ea56c0853b0b6990bddfcde1f..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/jobs.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<table align="center" class="mainpaneitem">
-  <tr>
-    <td class="formHeading" colspan=2>Current Background Jobs</td>
-  </tr>
-  <tr>
-    <td>
-      
-      <tmpl_if items>
-      
-        <table cellspacing=0 cellpadding=5 align=center border=1>
-
-          <tr>
-            <td><b>Secs From Now</b></td>
-            <td><b>Description</b></td>
-          </tr>
-
-          <tmpl_loop items>
-            <tr>
-              <td><tmpl_var future></td>
-              <td><tmpl_var description></td>
-            </tr>
-          </tmpl_loop>
-
-        </table>
-      
-      <tmpl_else>
-      
-        <tr><td colspan=2 align=center><i>(No current jobs)</i></td></tr>
-      
-      </tmpl_if>
-
-    </td>
-  </tr>
-</table>
diff --git a/apps/q/java/qresources/html/main.html b/apps/q/java/qresources/html/main.html
deleted file mode 100644
index b0941de6a5505e95ebb8ddce4550075e88b8ba9e..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/main.html
+++ /dev/null
@@ -1,122 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-<head>
-<TITLE>Q Web Interface</TITLE>
-<style type="text/css">
-<!--
-body { font-family: helvetica, arial, sans-serif; background-color: #000080 }
-td {  }
-code { font-family: courier, monospace; font-weight: bolder; font-size:smaller }
-.logocell {
-            border-color: #ffe000;
-            border-width: 1px;
-            border-top-style:none;
-            border-bottom-style:solid;
-            border-left-style:none;
-            border-right-style:none;
-            text-align: right;
-            font-weight: bold;
-            color: #f0f0ff;
-            }
-.tabcell {
-            font-weight: bold;
-            background-color: #fffff0;
-            padding:5px;
-            border-color:#ffe000;
-            border-width: 1px;
-            border-top-style:solid;
-            border-bottom-style:none;
-            border-left-style:solid;
-            border-right-style:solid;
-            }
-.tabcell_inactive {
-            background-color: #f0f0ff;
-            padding:5px;
-            border-color:#ffe000;
-            border-width: 1px;
-            border-top-style:solid;
-            border-bottom-style:solid;
-            border-left-style:solid;
-            border-right-style:solid;
-            }
-.tablink {
-            font-size: smaller;
-            text-decoration: none;
-            }
-.mainpane { border-color:#ffe000;
-            border-width: 1px;
-            border-top-style:none;
-            border-bottom-style:solid;
-            border-left-style:solid;
-            border-right-style:solid;
-            }
-
-.mainpaneitem1 { border-style:solid; border-color:#0000ff; border-width: thin; }
-
-.mainpaneitem { border-style:solid;
-            border-color:#0000ff;
-            border-width: thin;
-            background-color: #f0f0ff;
-            }
-
-.formHeading { font-size:larger; font-weight: bolder; text-align:center; }
-
-.btn1 {      font-weight: bold;
-            }
-
-input1 {     background-color: #fffff0;
-            color: #000080;
-            
-            }
-
--->
-</style>
-</head>
-<body bgcolor="#ffffff">
-<TABLE width="99%" height=99% align="center" cellspacing="0" cellpadding="0" border=0>
-  <TR>
-    <TD>
-      <table width="100%" cellspacing=0 cellpadding=0>
-        <tr>
-          <td>
-            <table align=right cellspacing=0 cellpadding=0 border=0>
-              <tr>
-                <tmpl_loop tabs>
-                  <td class=<tmpl_if active>"tabcell"<tmpl_else>"tabcell_inactive"</tmpl_if>>
-                    <a class="tablink" href="/<tmpl_var name>"><tmpl_var label></a>
-                  </td>
-                </tmpl_loop>
-                
-                <tmpl_if _ignore>
-                <TD class="tabcell_inactive">
-                  <a class="tablink" href="/status">Status</a>
-                </TD>
-                <TD class="tabcell">
-                  <a class="tablink" href="/settings">Settings</a>
-                </TD>
-                </tmpl_if>
-                
-              </tr>
-            </table>
-          </td>
-          <TD width=100%  class="logocell">
-            Q <tmpl_var nodeType> Node
-          </TD>
-        </TR>
-      </table>
-    </TD>  </TR>
-  <tr height=99% bgcolor="#fffff0">
-    <TD class="mainpane">
-      <br>
-      <center>
-      <tmpl_loop items>
-        <br>
-        <tmpl_var item>
-        <br>
-      </tmpl_loop>
-      </center>
-    </TD>
-  </tr>
-</TABLE>
-</body>
-</html>
diff --git a/apps/q/java/qresources/html/msiealert.html b/apps/q/java/qresources/html/msiealert.html
deleted file mode 100644
index 01ddecbd50f4c3e8470a8cba1f3700c85e0f66ef..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/msiealert.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<table align="center" class="mainpaneitem"  width=80%>
-  <tr>
-    <td class="formHeading" colspan=2>Critical Anonymity Alert!</td>
-  </tr>
-  <tr>
-    <td>
-      <table cellspacing=0 cellpadding=5 align=center border=1>
-
-        <tr>
-          <td>
-            <p>You are attempting to view a QSite via the Internet Explorer web browser.
-              </p>
-            <p>We have blocked the connection to protect your anonymity.
-              </p>
-            <p>As a matter of policy, Q does not support QSite browsing via Microsoft
-               Internet Explorer (MSIE), because of that browser's abysmal track record with
-               regard to security. If we did allow you to browse Q via MSIE, it would
-               be easy for a malicious QSite author to embed hostile content which 
-               undermines your computer's security and compromises your anonymity.
-               </p>
-             <p>If you want to surf I2P QSites, you'll need to use a more secure web
-                browser such as <a href="http://www.mozilla.org">Mozilla or Firefox</a>.
-          </td>
-        </tr>
-
-      </table>
-    </td>
-  </tr>
-</table>
diff --git a/apps/q/java/qresources/html/puterror.html b/apps/q/java/qresources/html/puterror.html
deleted file mode 100644
index 9b184d9d6f696d571419fd7d2739d20198298dee..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/puterror.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<table align="center" class="mainpaneitem">
-  <tr>
-    <td class="formHeading">Error Inserting <tmpl_if is_site>QSite<tmpl_else>Item</tmpl_if></td>
-  </tr>
-  <tr>
-    <td align=center>Your insert failed because:<br>
-      <code><tmpl_var error></code>
-    </td>
-  </tr>
-</table>
diff --git a/apps/q/java/qresources/html/putform.html b/apps/q/java/qresources/html/putform.html
deleted file mode 100644
index 7ceaa3c668b0455fd284b5225228236c359f4265..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/putform.html
+++ /dev/null
@@ -1,139 +0,0 @@
-<TABLE align="center" class="mainpaneitem" width=90% >
-  <TR>
-    <TD class="formHeading" colspan="2">Insert Content</TD>
-  </tr>
-
-  <TR>
-    <TD colspan="2" align=center><a href="/insert?mode=site">Insert A QSite Instead</a></TD>
-  </tr>
-
-  <tr><td colspan=2><hr></td></tr>
-
-  <form method="POST" ENCTYPE="multipart/form-data" action="/insert" class="formBody">
-<!--  <form method="POST" action="/insert" class="formBody"> -->
-
-    <input type=hidden name="cmd" value="put">
-
-    <tr>
-      <td valign=top>Type: </td>
-      <td style="font-size:smaller">
-        <input type="radio" name="type" value="text">Text 
-        <input type="radio" name="type" value="html">HTML
-        <input type="radio" name="type" value="image">Image
-        <input type="radio" name="type" value="audio">Audio
-        <input type="radio" name="type" value="video">Video
-        <input type="radio" name="type" value="archive">Archive
-        <input type="radio" name="type" value="other" checked>Other
-      </td>
-    </tr>
-    
-    <tr><td colspan=2><hr></td></tr>
-
-    <tr>
-      <TD valign=top>Title: </TD>
-      <td>
-        <input type="text" name="title" size="60" maxlength="58">
-        <div style="font-style: italic; font-size: smaller">
-          (A short descriptive title for this item)
-        </div>
-      </td>
-    </TR>
-
-    <tr><td colspan=2><hr></td></tr>
-
-    <tr>
-      <TD valign=top>Path: </TD>
-      <td>
-        <input type="text" name="path" size="60" maxlength="128">
-        <div style="font-style: italic; font-size: smaller">
-          (If you're inserting in plain-hash mode, this should be a suggested
-           filename for people to save the item as when downloading it; If you're
-           inserting in signed-space mode, this can be a longer pathname)
-        </div>
-      </td>
-    </TR>
-
-    <tr><td colspan=2><hr></td></tr>
-
-    <tr>
-      <TD valign=top>Mimetype: </TD>
-      <td>
-        <input type="text" name="mimetype" size="40" maxlength="40">
-        <div style="font-style: italic; font-size: smaller">
-          (Leave blank unless you know exactly what this means)
-        </div>
-      </td>
-    </TR>
-
-    <tr><td colspan=2><hr></td></tr>
-
-    <tr>
-      <TD valign=top>Keywords: </TD>
-      <td>
-        <input type="text" name="keywords" size="60" maxlength="80">
-        <div style="font-style: italic; font-size: smaller">
-          (Comma-separated list of short descriptive keywords)
-        </div>
-      </td>
-    </TR>
-
-    <tr><td colspan=2><hr></td></tr>
-
-    <tr>
-      <TD valign=top>Abstract: </TD>
-      <td>
-        <textarea name="abstract" cols="50" rows="2"></textarea>
-        <div style="font-style: italic; font-size: smaller">
-          (A short descriptive summary for the item, preferably no
-           longer than 256 characters)
-        </div>
-      </td>
-    </TR>
-
-    <tr><td colspan=2><hr></td></tr>
-
-    <tr>
-      <TD valign=top>File: </TD>
-      <td>
-        <INPUT type="file" name="data" size="40" maxlength="128">
-        <div style="font-style: italic; font-size: smaller">
-          (Leave this blank if you are entering the raw data in the text box below)
-        </div>
-      </td>
-    </TR>
-
-    <tr><td colspan=2><hr></td></tr>
-
-    <tr>
-      <TD valign=top>Private Key: </TD>
-      <td>
-        <INPUT type="text" name="privkey" size="30" maxlength="60">
-        <div style="font-size: smaller; font-style: italic">
-          (Only if inserting in signed space mode)
-        </div>
-      </td>
-    </TR>
-
-    <tr><td colspan=2><hr></td></tr>
-
-    <tr>
-      <TD colspan=2 align=center>
-        <INPUT class="btn" type="submit" name="submit" value="Insert it">
-      </TD>
-    </tr>
-
-    <tr><td colspan=2><hr></td></tr>
-
-    <tr>
-      <td valign=top>Raw Data:</td>
-      <td>
-        <div style="font-size: smaller; font-style:italic">
-          (You may enter the raw data here as text instead of selecting a
-           file above)
-        </div>
-        <textarea name="rawdata" cols=60 rows=16></textarea>
-      </td>
-    </tr>
-  </form>
-
-</TABLE>
\ No newline at end of file
diff --git a/apps/q/java/qresources/html/putok.html b/apps/q/java/qresources/html/putok.html
deleted file mode 100644
index 0a4e8b97e51869526da4cde73421558cbd9c7a9b..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/putok.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<table align="center" class="mainpaneitem">
-  <tr>
-    <td class="formHeading"><tmpl_if is_site>QSite<tmpl_else>Item</tmpl_if> Inserted Successfully</td>
-  </tr>
-  <tr>
-    <td align=center>Item URI:<br>
-      <code><input type="text" size=60 readonly value="<tmpl_var uri>"></code>
-    </td>
-  </tr>
-</table>
\ No newline at end of file
diff --git a/apps/q/java/qresources/html/putsiteform.html b/apps/q/java/qresources/html/putsiteform.html
deleted file mode 100644
index 20a0966f9b3d5bbcd033ba1491285c18380a80ee..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/putsiteform.html
+++ /dev/null
@@ -1,101 +0,0 @@
-<table align="center" class="mainpaneitem" width=90% >
-  <TR>
-    <TD class="formHeading" colspan="2">Insert A QSite</TD>
-  </tr>
-
-  <TR>
-    <TD colspan="2" align=center><a href="/insert?mode=file">Insert A Single File Instead</a></TD>
-  </tr>
-
-  <tr><td colspan=2><hr></td></tr>
-
-  <form method="POST" action="/insert" class="formBody">
-<!--  <form method="POST" action="/insert" class="formBody"> -->
-
-    <input type=hidden name="cmd" value="putsite">
-    <input type=hidden name="type" value="qsite">
-
-    <tr>
-      <TD valign=top>Name: </TD>
-      <td>
-        <input type="text" name="name" size="32" maxlength="50">
-        <div style="font-style: italic; font-size: smaller">
-          (A short name for the QSite - should contain only alphanumerics, '-', and '_';
-          should definitely <i>not</i> contain '/', ':', '\', ' ' etc)
-        </div>
-      </td>
-    </TR>
-
-    <tr><td colspan=2><hr></td></tr>
-
-    <tr>
-      <TD valign=top>Private Key: </TD>
-      <td>
-        <INPUT type="text" name="privkey" size="30" maxlength="60">
-        <div style="font-size: smaller; font-style: italic">
-          (Mandatory - if you don't have a signed-space keypair, get one by
-           clicking on <b>Tools</b>)
-        </div>
-      </td>
-    </TR>
-
-    <tr><td colspan=2><hr></td></tr>
-
-    <tr>
-      <TD valign=top>Directory: </TD>
-      <td>
-        <INPUT type="text" name="dir" size="60" maxlength="128">
-        <div style="font-size: smaller; font-style: italic">
-          (Absolute directory path where the QSite's files reside)
-        </div>
-      </td>
-    </TR>
-
-    <tr><td colspan=2><hr></td></tr>
-
-    <tr>
-      <TD valign=top>Title: </TD>
-      <td>
-        <input type="text" name="title" size="60" maxlength="58">
-        <div style="font-style: italic; font-size: smaller">
-          (A short descriptive title for this QSite)
-        </div>
-      </td>
-    </TR>
-
-    <tr><td colspan=2><hr></td></tr>
-
-    <tr>
-      <TD valign=top>Keywords: </TD>
-      <td>
-        <input type="text" name="keywords" size="60" maxlength="80">
-        <div style="font-style: italic; font-size: smaller">
-          (Comma-separated list of short descriptive keywords)
-        </div>
-      </td>
-    </TR>
-
-    <tr><td colspan=2><hr></td></tr>
-
-    <tr>
-      <TD valign=top>Abstract: </TD>
-      <td>
-        <textarea name="abstract" cols="50" rows="2"></textarea>
-        <div style="font-style: italic; font-size: smaller">
-          (A short descriptive summary for the QSite, preferably no
-           longer than 256 characters)
-        </div>
-      </td>
-    </TR>
-
-    <tr><td colspan=2><hr></td></tr>
-
-    <tr>
-      <TD colspan=2 align=center>
-        <INPUT class="btn" type="submit" name="submit" value="Insert it">
-      </TD>
-    </tr>
-
-  </form>
-
-</table>
diff --git a/apps/q/java/qresources/html/searchform.html b/apps/q/java/qresources/html/searchform.html
deleted file mode 100644
index a40d6a55a61cb52885f7909f7c61094d608e91ea..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/searchform.html
+++ /dev/null
@@ -1,76 +0,0 @@
-<TABLE align="center" class="mainpaneitem">
-  <TR>
-    <TD class="formHeading" colspan="3">Search For Content</TD>
-  </tr>
-
-  <form method="GET" action="/search" class="formBody">
-
-    <input type=hidden name=cmd value="search">
-
-    <tr>
-      <td valign=top>Type: </td>
-      <td style="font-size:smaller" colspan=2>
-        <input type="radio" name="type" value="any" checked>ANY
-        <input type="radio" name="type" value="qsite">QSite
-        <input type="radio" name="type" value="text">Text 
-        <input type="radio" name="type" value="html">HTML
-        <input type="radio" name="type" value="image">Image
-        <br>
-        <input type="radio" name="type" value="audio">Audio
-        <input type="radio" name="type" value="video">Video
-        <input type="radio" name="type" value="archive">Archive
-        <input type="radio" name="type" value="other">Other
-      </td>
-    </tr>
-
-    <tr>
-      <td>Title: </td>
-      <td><input type="text" name="title" size="60" value="<tmpl_var title>" maxlength="128"></td>
-      <td><input type="checkbox" name="title_re" <tmpl_if title_re>checked</tmpl_if>>regexp</td>
-    </tr>
-
-    <tr>
-      <TD>Path: </TD>
-      <td><INPUT type="text" name="path" size="60" value="<tmpl_var title>" maxlength="128"></td>
-      <td><input type="checkbox" name="path_re" <tmpl_if path_re>checked</tmpl_if>>regexp</td>
-    </TR>
-
-    <tr>
-      <TD>Mimetype: </TD>
-      <td><INPUT type="text" name="mimetype" size="40" maxlength="40" value="<tmpl_var mimetype>"></td>
-      <td><input type="checkbox" name="mimetype_re" <tmpl_if mimetype_re>checked</tmpl_if>>regexp</td>
-    </TR>
-
-    <tr>
-      <TD>Keywords: </TD>
-      <td><INPUT type="text" name="keywords" size="60" maxlength="80" value="<tmpl_var keywords>"></td>
-      <td><input type="checkbox" name="keywords_re" <tmpl_if keywords_re>checked</tmpl_if>>regexp</td>
-    </TR>
-
-    <tr>
-      <TD>Summary: </TD>
-      <td><textarea name="summary" cols="50" rows=2><tmpl_var summary></textarea></td>
-      <td><input type="checkbox" name="summary_re" <tmpl_if summary_re>checked</tmpl_if>>regexp</td>
-    </TR>
-
-    <tr>
-      <td>
-        Search Mode:
-      </td>
-      <td>
-        <input type=radio name="searchmode" value="and" checked>AND 
-        <input type=radio name="searchmode" value="or">OR
-      </td>
-    </tr>
-    
-    <tr><td colspan=3><hr></td></tr>
-
-    <tr>
-      <TD colspan=3 align=center>
-        <INPUT class="btn" type="submit" name="submit" value="Search!">
-      </TD>
-    </tr>
-
-  </form>
-
-</TABLE>
\ No newline at end of file
diff --git a/apps/q/java/qresources/html/searchresults.html b/apps/q/java/qresources/html/searchresults.html
deleted file mode 100644
index 21ac24552d51850b27b3d10af4b709e4367e43f7..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/searchresults.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<table align="center" class="mainpaneitem" <tmpl_if results>width="95%"</tmpl_if>>
-  <tr>
-    <td class="formHeading" colspan=2>Search Results</td>
-  </tr>
-  <tr>
-    <td style="font-style: italic" align="center"><tmpl_var numresults> items found</td>
-  </tr>
-  <tr>
-    <td width="90%">
-      <table cellspacing=0 cellpadding=5 align=center border=0 width=95%>
-        <tmpl_loop results>
-          <tr>
-            <td>
-              <div style="font-size: larger"><a href="/<tmpl_var uri>"><tmpl_var title></a></div>
-              <tmpl_if abstract>
-                <div style="font-style: italic"><tmpl_var abstract></div>
-              </tmpl_if>
-              <div style="font-size: smaller; color: #008000;"><tmpl_var uri></div>
-              <div style="font-size:smaller;"><tmpl_var type> (<tmpl_var mimetype>) <tmpl_var size> bytes</div>
-              <div/>
-            </td>
-          </tr>
-        </tmpl_loop>
-      </table>
-    </td>
-  </tr>
-</table>
diff --git a/apps/q/java/qresources/html/settings.html b/apps/q/java/qresources/html/settings.html
deleted file mode 100644
index c6686e90f5f0b923c83ac5c8735fa99af3c8c096..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/settings.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<table align="center" class="mainpaneitem">
-  <tr>
-    <td class="formHeading">Q Settings</td>
-  </tr>
-  <tr>
-    <td>Not implemented yet</td>
-  </tr>
-</table>
-
diff --git a/apps/q/java/qresources/html/status.html b/apps/q/java/qresources/html/status.html
deleted file mode 100644
index f0fac2d6e6a7f418565c250a27f375be3fdfce05..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/status.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<table align="center" class="mainpaneitem">
-  <tr>
-    <td class="formHeading" colspan=2>Q Node Status</td>
-  </tr>
-  <tr>
-    <td>
-      <table cellspacing=0 cellpadding=5 align=center border=1>
-
-        <tr>
-          <td><b>Item</b></td>
-          <td><b>Value</b></td>
-        </tr>
-
-        <tmpl_loop items>
-          <tr>
-            <td><tmpl_var key></td>
-            <td><tmpl_var value></td>
-          </tr>
-        </tmpl_loop>
-
-      </table>
-    </td>
-  </tr>
-</table>
diff --git a/apps/q/java/qresources/html/tools.html b/apps/q/java/qresources/html/tools.html
deleted file mode 100644
index 84aecb3fbaac8cc02888186f854ebd007f5f4228..0000000000000000000000000000000000000000
--- a/apps/q/java/qresources/html/tools.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<table align="center" class="mainpaneitem">
-  <tr>
-    <td class="formHeading">Q Tools</td>
-  </tr>
-</table>
-
diff --git a/apps/q/java/qresources/html/widgets/itemtype.html b/apps/q/java/qresources/html/widgets/itemtype.html
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/apps/q/java/src/HTML/Template.java b/apps/q/java/src/HTML/Template.java
deleted file mode 100644
index 6b6ce914aa3469791f98198728e6694a093bf0fc..0000000000000000000000000000000000000000
--- a/apps/q/java/src/HTML/Template.java
+++ /dev/null
@@ -1,1131 +0,0 @@
-/*
-*      HTML.Template:  A module for using HTML Templates with java
-*
-*      Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
-*
-*      This module is free software; you can redistribute it
-*      and/or modify it under the terms of either:
-*
-*      a) the GNU General Public License as published by the Free
-*      Software Foundation; either version 1, or (at your option)
-*      any later version, or
-*
-*      b) the "Artistic License" which comes with this module.
-*
-*      This program is distributed in the hope that it will be
-*      useful, but WITHOUT ANY WARRANTY; without even the implied
-*      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-*      PURPOSE.  See either the GNU General Public License or the
-*      Artistic License for more details.
-*
-*      You should have received a copy of the Artistic License
-*      with this module, in the file ARTISTIC.  If not, I'll be
-*      glad to provide one.
-*
-*      You should have received a copy of the GNU General Public
-*      License along with this program; if not, write to the Free
-*      Software Foundation, Inc., 59 Temple Place, Suite 330,
-*      Boston, MA 02111-1307 USA
-*
-* Modified by David McNab (david@rebirthing.co.nz) to allow nesting of
-* templates (ie, passing a child Template object as a value argument
-* to a .setParam() invocation on a parent Template object).
-*
-*/
-
-package HTML;
-import java.io.BufferedReader;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.Reader;
-import java.util.EmptyStackException;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.NoSuchElementException;
-import java.util.Properties;
-import java.util.Stack;
-import java.util.StringTokenizer;
-import java.util.Vector;
-
-import HTML.Tmpl.Filter;
-import HTML.Tmpl.Util;
-import HTML.Tmpl.Element.Conditional;
-import HTML.Tmpl.Element.Element;
-import HTML.Tmpl.Element.If;
-import HTML.Tmpl.Element.Var;
-import HTML.Tmpl.Parsers.Parser;
-
-/**
- * Use HTML Templates with java.
- * <p>
- * The HTML.Template class allows you to use HTML Templates from within
- * your java programs.  It makes it possible to change the look of your
- * servlets without having to recompile them.  Use HTML.Template to 
- * separate code from presentation in your servlets.
- * <p>
- * <pre>
- *	Hashtable args = new Hashtable();
- *	args.put("filename", "my_template.tmpl");
- *
- *	Template t = new Template(args);
- *
- *	t.setParam("title", "The HTML Template package");
- *	t.printTo(response.getWriter());
- * </pre>
- * <p>
- * HTML.Template is based on the perl module HTML::Template by Sam Tregar
- * <p>
- * Modified by David McNab (david@rebirthing.co.nz) to allow nesting of
- * templates (ie, passing a child Template object as a value argument
- * to a .setParam() invocation on a parent Template object).
- * </p>
- * @author	Philip S Tellis
- * @version	0.1.2
- */
-public class Template 
-{
-	private If __template__ = new If("__template__");
-	private Hashtable params = new Hashtable();
-
-	private boolean dirty = true;
-
-	private boolean strict = true;
-	private boolean die_on_bad_params = false;
-	private boolean global_vars = false;
-	private boolean case_sensitive = false;
-	private boolean loop_context_vars = false;
-	private boolean debug = false;
-	private boolean no_includes = false;
-	private boolean search_path_on_include = false;
-	private int max_includes = 11;
-	private String filename = null;
-	private String scalarref = null;
-	private String [] arrayref = null;
-	private String [] path = null;
-	private Reader filehandle = null;
-	private Filter [] filters = null;
-
-	private Stack elements = new Stack();
-	private Parser parser;
-
-	/**
-	 * Initialises a new HTML.Template object with the contents of
-	 * the given file.
-	 *
-	 * @param filename			a string containing the name of 
-	 *					the file to be used as a 
-	 *					template.  This may be an 
-	 *					absolute or relative path to a 
-	 *					template file.
-	 *
-	 * @throws FileNotFoundException	If the file specified does not 
-	 *					exist.
-	 * @throws IllegalStateException	If &lt;tmpl_include&gt; is
-	 *					used when no_includes is in
-	 *					effect.
-	 * @throws IOException			If an input or output Exception 
-	 *					occurred while reading the 
-	 *					template.
-	 *
-	 * @deprecated No replacement.  You should use either
-	 *				{@link #Template(Object [])} or
-	 *				{@link #Template(Hashtable)}
-	 */
-	public Template(String filename)
-			throws FileNotFoundException, 
-				IllegalStateException,
-				IOException
-	{
-		this.filename = filename;
-		init();
-	}
-
-        
-	/**
-	 * Initialises a new Template object, using the name/value
-	 * pairs passed as default values.
-	 * <p>
-	 * The parameters passed may be any combination of filename, 
-	 * scalarref, arrayref, path, case_sensitive, loop_context_vars, 
-	 * strict, die_on_bad_params, global_vars, max_includes, 
-	 * no_includes, search_path_on_include and debug.
-	 * Each with its own value.  Any one of filename, scalarref or
-	 * arrayref must be passed.
-	 * <p>
-	 * Eg:
-	 * <pre>
-	 *	String [] template_init = {
-	 *		"filename",  "my_template.tmpl",
-	 *		"case_sensitive", "true",
-	 *		"max_includes",   "5"
-	 *	};
-	 *
-	 *      Template t = new Template(template_init);
-	 * </pre>
-	 * <p>
-	 * The above code creates a new Template object, initialising
-	 * its input file to my_template.tmpl, turning on case_sensitive
-	 * parameter matching, and restricting maximum depth of includes
-	 * to five.
-	 * <p>
-	 * Parameter values that take boolean values may either be a String
-	 * containing the words true/false, or the Boolean values Boolean.TRUE
-	 * and Boolean.FALSE.  Numeric values may be Strings, or Integers.
-	 *
-	 * @since 0.0.8
-	 *
-	 * @param args		an array of name/value pairs to initialise
-	 *			this template with.  Valid values for
-	 *			each element may be:
-	 * @param filename	[Required] a String containing the path to a 
-	 *			template file
-	 * @param scalarref	[Required] a String containing the entire 
-	 *			template as its contents
-	 * @param arrayref	[Required] an array of lines that make up
-	 *			the template
-	 * @param path		[Optional] an array of Strings specifying
-	 *			the directories in which to look for the
-	 *			template file.  If not specified, the current
-	 *			working directory is used.  If specified,
-	 *			only the directories in this array are used.
-	 *			If you want the current directory searched,
-	 *			include "." in the path.
-	 *			<p>
-	 *			If you have only a single path, it can be a
-	 *			plain String instead of a String array.
-	 *			<p>
-	 *			This is effective only for the template file,
-	 *			and not for included files, but see
-	 *			search_path_on_include for how to change that.
-	 * @param case_sensitive	[Optional] specifies whether parameter 
-	 *			matching is case sensitive or not.  A value 
-	 *			of "false", "0" or "" is considered false.  
-	 *			All other values are true.
-	 *			<p>
-	 *			Default: false
-	 * @param loop_context_vars	[Optional] when set to true four loop
-	 * 			context variables are made available inside a 
-	 *			loop: <code>__FIRST__, __LAST__, __INNER__, __ODD__, __COUNTER__</code>.
-	 *			They can be used with <code>&lt;TMPL_IF&gt;</code>, 
-	 *			<code>&lt;TMPL_UNLESS&gt;</code> and <code>&lt;TMPL_ELSE&gt;</code> to 
-	 *			control how a loop is output. Example:
-	 *			<pre>
-	 *	    &lt;TMPL_LOOP NAME="FOO"&gt;
-	 *	       &lt;TMPL_IF NAME="__FIRST__"&gt;
-	 *	         This only outputs on the first pass.
-	 *	       &lt;/TMPL_IF&gt;
-	 *
-	 *	       &lt;TMPL_IF NAME="__ODD__"&gt;
-	 *	         This outputs on the odd passes.
-	 *	       &lt;/TMPL_IF&gt;
-	 *
-	 *	       &lt;TMPL_UNLESS NAME="__ODD__"&gt;
-	 *	         This outputs on the even passes.
-	 *	       &lt;/TMPL_IF&gt;
-	 *
-	 *	       &lt;TMPL_IF NAME="__INNER__"&gt;
-	 *	         This outputs on passes that are 
-	 *		neither first nor last.
-	 *	       &lt;/TMPL_IF&gt;
-	 *
-	 *	       &lt;TMPL_IF NAME="__LAST__"&gt;
-	 *	         This only outputs on the last pass.
-	 *	       &lt;TMPL_IF&gt;
-	 *	    &lt;/TMPL_LOOP&gt;
-	 *			</pre>
-	 *			<p>
-	 *			NOTE: A loop with only a single pass will get 
-	 *			both <code>__FIRST__</code> and <code>__LAST__</code>
-	 *			set to true, but not <code>__INNER__</code>.
-	 *			<p>
-	 *			Default: false
-	 * @param strict	[Optional] if set to false the module will 
-	 *			allow things that look like they might be 
-	 *			TMPL_* tags to get by without throwing
-	 *			an exception.  Example:
-	 *			<pre>
-	 *          &lt;TMPL_HUH NAME=ZUH&gt;
-	 *			</pre>
-	 *			<p>
-	 *			Would normally cause an error, but if you 
-	 *			create the Template with strict == 0, 
-	 *			HTML.Template will ignore it.
-	 *			<p>
-	 *			Default: true
-	 * @param die_on_bad_params		[Optional] if set to true
-	 *			the module will complain if you try to set
-	 *			tmpl.setParam("param_name", "value") and
-	 *			param_name doesn't exist in the template.
-	 *			<p>
-	 *			This effect doesn't descend into loops. 
-	 *			<p>
-	 *			Default: false (may change in later versions)
-	 * @param global_vars	[Optional] normally variables declared outside
-	 *			a loop are not available inside a loop.  This
-	 *			option makes TMPL_VARs global throughout
-	 *			the template.  It also affects TMPL_IF and TMPL_UNLESS.
-	 *			<pre>
-	 *	    &lt;p&gt;This is a normal variable: &lt;TMPL_VAR NORMAL&gt;.&lt;/p&gt;
-	 *
-	 *	    &lt;TMPL_LOOP NAME="FROOT_LOOP&gt;
-	 *	       Here it is inside the loop: &lt;TMPL_VAR NORMAL&gt;
-	 *	    &lt;/TMPL_LOOP&gt;
-	 *			</pre>
-	 *			<p>
-	 *			Normally this wouldn't work as expected, since
-	 *			&lt;TMPL_VAR NORMAL&gt;'s value outside the loop
-	 *			isn't available inside the loop.
-	 *			<p>
-	 *			Default: false (may change in later versions)
-	 * @param max_includes	[Optional] specifies the maximum depth that
-	 *			includes can reach.  Including files to a 
-	 *			depth greater than this value causes an error 
-	 *			message to be displayed.  Set to 0 to disable 
-	 *			this protection.
-	 *			<p>
-	 *			Default: 10
-	 * @param no_includes	[Optional] If set to true, disallows the
-	 *			&lt;TMPL_INCLUDE&gt; tag in the template
-	 *			file.  This can be used to make opening 
-	 *			untrusted templates slightly less dangerous.
-	 *			<p>
-	 *			Default: false
-	 * @param search_path_on_include	[Optional] if set, then the
-	 *			path is searched for included files as well
-	 *			as the template file.  See the path parameter
-	 *			for more information.
-	 *			<p>
-	 *			Default: false
-	 * @param debug		[Optional] setting this option to true causes
-	 *			HTML.Template to print random error messages
-	 *			to STDERR.
-	 *			
-	 * @throws ArrayIndexOutOfBoundsException	If an odd number of
-	 *					parameters is passed.
-	 * @throws FileNotFoundException	If the file specified does not 
-	 *					exist or no filename is passed.
-	 * @throws IllegalArgumentException	If an unknown parameter is
-	 *					passed.
-	 * @throws IllegalStateException	If &lt;tmpl_include&gt; is
-	 *					used when no_includes is in
-	 *					effect.
-	 * @throws IOException			If an input or output Exception 
-	 *					occurred while reading the 
-	 *					template.
-	 */
-	public Template(Object [] args)
-			throws ArrayIndexOutOfBoundsException,
-				FileNotFoundException,
-				IllegalArgumentException,
-				IllegalStateException,
-				IOException
-				
-	{
-		if(args.length%2 != 0)
-			throw new ArrayIndexOutOfBoundsException("odd number " +
-					"of arguments passed");
-
-		for(int i=0; i<args.length; i+=2) {
-			parseParam((String)args[i], args[i+1]);
-		}
-
-		init();
-	}
-
-	/**
-	 * Initialises a new Template object, using the values in the
-	 * Hashtable args as defaults.
-	 * <p>
-	 * The parameters passed are the same as in the Template(Object [])
-	 * constructor. Each with its own value.  Any one of filename, 
-	 * scalarref or arrayref must be passed.
-	 * <p>
-	 * Eg:
-	 * <pre>
-	 *	Hashtable args = new Hashtable();
-	 *	args.put("filename", "my_template.tmpl");
-	 *	args.put("case_sensitive", "true");
-	 *	args.put("loop_context_vars", Boolean.TRUE);
-	 *	// args.put("max_includes", "5");
-	 *	args.put("max_includes", new Integer(5));
-	 *
-	 *	Template t = new Template(args);
-	 * </pre>
-	 * <p>
-	 * The above code creates a new Template object, initialising
-	 * its input file to my_template.tmpl, turning on case_sensitive
-	 * parameter matching, and the loop context variables __FIRST__,
-	 * __LAST__, __ODD__ and __INNER__, and restricting maximum depth of 
-	 * includes to five.
-	 * <p>
-	 * Parameter values that take boolean values may either be a String
-	 * containing the words true/false, or the Boolean values Boolean.TRUE
-	 * and Boolean.FALSE.  Numeric values may be Strings, or Integers.
-	 *
-	 * @since 0.0.10
-	 *
-	 * @param args		a Hashtable of name/value pairs to initialise
-	 *			this template with.  Valid values are the same
-	 *			as in the Template(Object []) constructor.
-	 *			
-	 * @throws FileNotFoundException	If the file specified does not 
-	 *					exist or no filename is passed.
-	 * @throws IllegalArgumentException	If an unknown parameter is
-	 *					passed.
-	 * @throws IllegalStateException	If &lt;tmpl_include&gt; is
-	 *					used when no_includes is in
-	 *					effect.
-	 * @throws IOException			If an input or output Exception 
-	 *					occurred while reading the 
-	 *					template.
-	 *
-	 * @see #Template(Object [])
-	 */
-	public Template(Hashtable args)
-			throws FileNotFoundException,
-				IllegalArgumentException,
-				IllegalStateException,
-				IOException
-				
-	{
-		Enumeration e = args.keys();
-		while(e.hasMoreElements()) {
-			String key = (String)e.nextElement();
-			Object value = args.get(key);
-
-			parseParam(key, value);
-		}
-
-		init();
-	}
-
-	/**
-	 * Prints the parsed template to the provided PrintWriter.
-	 *
-	 * @param out	the PrintWriter that this template will be printed
-	 *		to
-	 */
-	public void printTo(PrintWriter out) 
-	{
-		out.print(output());
-	}
-
-	/**
-	 * Returns the parsed template as a String.
-	 *
-	 * @return	a string containing the parsed template
-	 */
-	public String output()
-	{
-		return __template__.parse(params);
-	}
-
-	/**
-	 * Sets the values of parameters in this template from a Hashtable.
-	 *
-	 * @param params	a Hashtable containing name/value pairs for
-	 *			this template.  Keys in this hashtable must
-	 *			be Strings and values may be either Strings
-	 *			or Vectors.
-	 *			<p>
-	 *			Parameter names are currently not case
-	 *			sensitive.
-	 *			<p>
-	 *			Parameter names can contain only letters, 
-	 *			digits,	., /, +, - and _ characters.
-	 *			<p>
-	 *			Parameter names starting and ending with
-	 *			a double underscore are not permitted.
-	 *			eg: <code>__myparam__</code> is illegal.
-	 *
-	 * @return		the number of parameters actually set.
-	 *			Illegal parameters will not be set, but
-	 *			no error/exception will be thrown.
-	 */
-	public int setParams(Hashtable params) 
-	{
-		if(params == null || params.isEmpty())
-			return 0;
-		int count=0;
-		for(Enumeration e = params.keys(); e.hasMoreElements();) {
-			Object key = e.nextElement();
-			if(key.getClass().getName().endsWith(".String")) {
-				Object value = params.get(key);
-				try {
-					setParam((String)key, value);
-					count++;
-				} catch (Exception pe) {
-					// key was not a String or Vector
-					// or key was null
-					// don't increment count
-				}
-			}
-		}
-		if(count>0) {
-			dirty=true;
-			Util.debug_print("Now dirty: set params");
-		}
-
-		return count;
-	}
-
-	/**
-	 * Sets a single scalar parameter in this template.
-	 *
-	 * @param name	a String containing the name of this parameter.
-	 *		Parameter names are currently not case sensitive.
-	 * @param value	a String containing the value of this parameter	
-	 *
-	 * @return				the value of the parameter set
-	 * @throws IllegalArgumentException 	if the parameter name contains
-	 *					illegal characters
-	 * @throws NullPointerException 	if the parameter name is null
-	 *
-	 * @see #setParams(Hashtable)
-	 */
-	public String setParam(String name, String value)
-			throws IllegalArgumentException, NullPointerException
-	{
-		try {
-			return (String)setParam(name, (Object)value);
-		} catch(ClassCastException iae) {
-			return null;
-		}
-	}
-
-	/**
-	 * Sets a single Integer parameter in this template.
-	 *
-	 * @param name	a String containing the name of this parameter.
-	 *		Parameter names are currently not case sensitive.
-	 * @param value	an Integer containing the value of this parameter	
-	 *
-	 * @return				the value of the parameter set
-	 * @throws IllegalArgumentException 	if the parameter name contains
-	 *					illegal characters
-	 * @throws NullPointerException 	if the parameter name is null
-	 *
-	 * @see #setParams(Hashtable)
-	 */
-	public Integer setParam(String name, Integer value)
-			throws IllegalArgumentException, NullPointerException
-	{
-		try {
-			return (Integer)setParam(name, (Object)value);
-		} catch(ClassCastException iae) {
-			return null;
-		}
-	}
-
-	/**
-	 * Sets a single int parameter in this template.
-	 *
-	 * @param name	a String containing the name of this parameter.
-	 *		Parameter names are currently not case sensitive.
-	 * @param value	an int containing the value of this parameter	
-	 *
-	 * @return				the value of the parameter set
-	 * @throws IllegalArgumentException 	if the parameter name contains
-	 *					illegal characters
-	 * @throws NullPointerException 	if the parameter name is null
-	 *
-	 * @see #setParams(Hashtable)
-	 */
-	public int setParam(String name, int value)
-			throws IllegalArgumentException, NullPointerException
-	{
-		return setParam(name, new Integer(value)).intValue();
-	}
-
-	/**
-	 * Sets a single boolean parameter in this template.
-	 *
-	 * @param name	a String containing the name of this parameter.
-	 *		Parameter names are currently not case sensitive.
-	 * @param value	a boolean containing the value of this parameter	
-	 *
-	 * @return				the value of the parameter set
-	 * @throws IllegalArgumentException 	if the parameter name contains
-	 *					illegal characters
-	 * @throws NullPointerException 	if the parameter name is null
-	 *
-	 * @see #setParams(Hashtable)
-	 */
-	public boolean setParam(String name, boolean value)
-			throws IllegalArgumentException, NullPointerException
-	{
-		return setParam(name, new Boolean(value)).booleanValue();
-	}
-
-	/**
-	 * Sets a single Boolean parameter in this template.
-	 *
-	 * @param name	a String containing the name of this parameter.
-	 *		Parameter names are currently not case sensitive.
-	 * @param value	a Boolean containing the value of this parameter	
-	 *
-	 * @return				the value of the parameter set
-	 * @throws IllegalArgumentException 	if the parameter name contains
-	 *					illegal characters
-	 * @throws NullPointerException 	if the parameter name is null
-	 *
-	 * @see #setParams(Hashtable)
-	 */
-	public Boolean setParam(String name, Boolean value)
-			throws IllegalArgumentException, NullPointerException
-	{
-		try {
-			return (Boolean)setParam(name, (Object)value);
-		} catch(ClassCastException iae) {
-			return null;
-		}
-	}
-
-
-	/**
-	 * Sets a single parameter in this template to a nested Template
-	 *
-	 * @param name	a String containing the name of this parameter.
-	 *		Parameter names are currently not case sensitive.
-	 * @param value	a Template object to be nested in
-	 *
-	 * @return				the value of the parameter set
-	 * @throws IllegalArgumentException 	if the parameter name contains
-	 *					illegal characters
-	 * @throws NullPointerException 	if the parameter name is null
-	 */
-	public Template setParam(String name, Template value)
-			throws IllegalArgumentException, NullPointerException
-	{
-		try {
-			return (Template)setParam(name, (Object)value);
-		} catch(ClassCastException iae) {
-			return null;
-		}
-	}
-
-
-	/**
-	 * Sets a single list parameter in this template.
-	 *
-	 * @param name	a String containing the name of this parameter.
-	 *		Parameter names are not currently case sensitive.
-	 * @param value	a Vector containing a list of Hashtables of parameters
-	 *
-	 * @return				the value of the parameter set
-	 * @throws IllegalArgumentException 	if the parameter name contains
-	 *					illegal characters
-	 * @throws NullPointerException 	if the parameter name is null
-	 *
-	 * @see #setParams(Hashtable)
-	 */
-	public Vector setParam(String name, Vector value) 
-			throws IllegalArgumentException, NullPointerException
-	{
-		try {
-			return (Vector)setParam(name, (Object)value);
-		} catch(ClassCastException iae) {
-			return null;
-		}
-	}
-
-	/**
-	 * Returns a parameter from this template identified by the given name.
-	 *
-	 * @param name	a String containing the name of the parameter to be 
-	 *		returned.  Parameter names are not currently case
-	 *		sensitive.
-	 *
-	 * @return	the value of the requested parameter.  If the parameter
-	 *		is a scalar, the return value is a String, if the 
-	 *		parameter is a list, the return value is a Vector.
-	 *
-	 * @throws NoSuchElementException	if the parameter does not exist
-	 *					in the template
-	 * @throws NullPointerException		if the parameter name is null
-	 */
-	public Object getParam(String name) 
-			throws NoSuchElementException, NullPointerException
-	{
-		if(name == null)
-			throw new NullPointerException("name cannot be null");
-		if(!params.containsKey(name))
-			throw new NoSuchElementException(name + 
-				" is not a parameter in this template");
-
-		if(case_sensitive)
-			return params.get(name);
-		else
-			return params.get(name.toLowerCase());
-	}
-
-
-	private void parseParam(String key, Object value)
-			throws IllegalStateException
-	{
-		if(key.equals("case_sensitive"))
-		{
-			this.case_sensitive=boolify(value);
-			Util.debug_print("case_sensitive: "+value);
-		}
-		else if(key.equals("strict"))
-		{
-			this.strict=boolify(value);
-			Util.debug_print("strict: "+value);
-		}
-		else if(key.equals("global_vars"))
-		{
-			this.global_vars=boolify(value);
-			Util.debug_print("global_vars: "+value);
-		}
-		else if(key.equals("die_on_bad_params"))
-		{
-			this.die_on_bad_params=boolify(value);
-			Util.debug_print("die_obp: "+value);
-		}
-		else if(key.equals("max_includes"))
-		{
-			this.max_includes=intify(value)+1;
-			Util.debug_print("max_includes: "+value);
-		}
-		else if(key.equals("no_includes"))
-		{
-			this.no_includes=boolify(value);
-			Util.debug_print("no_includes: "+value);
-		}
-		else if(key.equals("search_path_on_include"))
-		{
-			this.search_path_on_include=boolify(value);
-			Util.debug_print("path_includes: "+value);
-		}
-		else if(key.equals("loop_context_vars"))
-		{
-			this.loop_context_vars=boolify(value);
-			Util.debug_print("loop_c_v: "+value);
-		}
-		else if(key.equals("debug"))
-		{
-			this.debug=boolify(value);
-			Util.debug=this.debug;
-			Util.debug_print("debug: "+value);
-		}
-		else if(key.equals("filename"))
-		{
-			this.filename = (String)value;
-			Util.debug_print("filename: "+value);
-		}
-		else if(key.equals("scalarref"))
-		{
-			this.scalarref = (String)value;
-			Util.debug_print("scalarref");
-		}
-		else if(key.equals("arrayref"))
-		{
-			this.arrayref = (String [])value;
-			Util.debug_print("arrayref");
-		}
-		else if(key.equals("path"))
-		{
-			if(value.getClass().getName().startsWith("["))
-				this.path = (String [])value;
-			else {
-				this.path = new String[1];
-				this.path[0] = (String)value;
-			}
-			Util.debug_print("path");
-			for(int j=0; j<this.path.length; j++)
-				Util.debug_print(this.path[j]);
-		}
-		else if(key.equals("filter"))
-		{
-			if(value.getClass().getName().startsWith("["))
-				this.filters = (Filter [])value;
-			else {
-				this.filters = new Filter[1];
-				this.filters[0] = (Filter)value;
-			}
-			Util.debug_print("filters set: " + filters.length);
-		}
-		else if(key.equals("filehandle"))
-		{
-			this.filehandle = (Reader)value;
-			Util.debug_print("filehandle");
-		}
-		else
-		{
-			throw new IllegalArgumentException(
-					(String)key);
-		}
-
-	}
-
-	private void init()
-			throws FileNotFoundException, 
-				IllegalStateException,
-				IOException
-	{
-		if(this.filename == null && 
-				this.scalarref == null &&
-				this.arrayref == null &&
-				this.filehandle == null)
-			throw new FileNotFoundException("template filename required");
-		
-		Util.debug = this.debug;
-
-		params.put("__template__", "true");
-
-		String [] parser_params = {
-			"case_sensitive", 	stringify(case_sensitive),
-			"strict", 		stringify(strict),
-			"loop_context_vars", 	stringify(loop_context_vars),
-			"global_vars", 		stringify(global_vars)
-		};
-
-		parser = new Parser(parser_params);
-
-		if(this.filename != null)
-			read_file(filename);
-		else if(this.arrayref != null)
-			read_line_array(this.arrayref);
-		else if(this.scalarref != null)
-			read_line(this.scalarref);
-		else if(this.filehandle != null)
-			read_fh(this.filehandle);
-
-		if(!elements.empty())
-			System.err.println("stack not empty");
-	}
-
-
-	private Element parseLine(String line, Element e)
-			throws FileNotFoundException,
-				IllegalStateException,
-				IOException, 
-				EmptyStackException
-	{
-		Vector parts;
-
-		parts = parser.parseLine(line);
-		Util.debug_print("Items: " + parts.size());
-
-		for(Enumeration pt = parts.elements(); pt.hasMoreElements();) 
-		{
-			Object o = pt.nextElement();
-			
-			if(o.getClass().getName().endsWith(".String"))
-			{
-				if(((String)o).equals(""))
-					continue;
-
-				e.add((String)o);
-				Util.debug_print("added: " +(String)o);
-				continue;
-			}
-				
-			// if we come here, then it is an element
-
-			Properties p = (Properties)o;
-			String type=p.getProperty("type");
-			Util.debug_print("adding element: " + type);
-
-			if(type.equals("include")) 
-			{
-				if(no_includes)
-					throw new IllegalStateException(
-						"<tmpl_include> not " +
-						"allowed when " +
-						"no_includes in effect"
-					);
-				if(max_includes == 0) {
-					throw new IndexOutOfBoundsException(
-						"include too deep");
-				} else {
-					// come here if positive 
-					// or negative
-					elements.push(e);
-					read_file(p.getProperty("name"));
-				}
-			}
-			else if(type.equals("var"))
-			{
-				String name = p.getProperty("name");
-				String escape = p.getProperty("escape");
-				String def = p.getProperty("default");
-				Util.debug_print("name: " + name);
-				Util.debug_print("escape: " + escape);
-				Util.debug_print("default: " + def);
-				e.add(new Var(name, escape, def));
-			}
-			else if(type.equals("else")) 
-			{
-				Util.debug_print("adding branch");
-				((Conditional)e).addBranch();
-			}
-			else if(p.getProperty("close").equals("true")) 
-			{
-				Util.debug_print("closing tag");
-				if(!type.equals(e.Type()))
-					throw new EmptyStackException();
-
-				e = (Element)elements.pop();
-			} 
-			else
-			{
-				Element t = parser.getElement(p);
-				e.add(t);
-				elements.push(e);
-				e=t;
-			}
-		}
-		return e;
-	}
-
-	private void read_file(String filename)
-			throws FileNotFoundException, 
-				IllegalStateException,
-				IOException, 
-				EmptyStackException
-	{
-		BufferedReader br=openFile(filename);
-
-		String line;
-
-		Element e = null;
-		if(elements.empty())
-			e = __template__;
-		else
-			e = (Element)elements.pop();
-
-		max_includes--;
-		while((line=br.readLine()) != null) {
-			Util.debug_print("Line: " + line);
-			e = parseLine(line+"\n", e);
-		}
-		max_includes++;
-
-		br.close();
-		br=null;
-
-	}
-	
-	private void read_line_array(String [] lines)
-			throws FileNotFoundException, 
-				IllegalStateException,
-				IOException, 
-				EmptyStackException
-	{
-
-		Element e = __template__;
-
-		max_includes--;
-		for(int i=0; i<lines.length; i++) {
-			Util.debug_print(lines[i]);
-			e = parseLine(lines[i], e);
-		}
-		max_includes++;
-	}
-
-	private void read_line(String lines)
-			throws FileNotFoundException, 
-				IllegalStateException,
-				IOException, 
-				EmptyStackException
-	{
-
-		Element e = __template__;
-
-		max_includes--;
-		StringTokenizer st = new StringTokenizer(lines, "\n");
-		while(st.hasMoreTokens()) {
-			String line = st.nextToken();
-			Util.debug_print(line);
-			e = parseLine(line+"\n", e);
-		}
-		max_includes++;
-	}
-
-	private void read_fh(Reader handle)
-			throws FileNotFoundException, 
-				IllegalStateException,
-				IOException, 
-				EmptyStackException
-	{
-		BufferedReader br=new BufferedReader(handle);
-
-		String line;
-
-		Element e = null;
-		if(elements.empty())
-			e = __template__;
-		else
-			e = (Element)elements.pop();
-
-		max_includes--;
-		while((line=br.readLine()) != null) {
-			Util.debug_print("Line: " + line);
-			e = parseLine(line+"\n", e);
-		}
-		max_includes++;
-
-		br.close();
-		br=null;
-
-	}
-	
-	private Object setParam(String name, Object value)
-			throws ClassCastException,
-				NullPointerException, 
-				IllegalArgumentException
-	{
-		if(name==null)
-			throw new NullPointerException(
-					"parameter name cannot be null");
-
-
-		if(!Util.isNameChar(name)) {
-			throw new IllegalArgumentException("parameter name " +
-				"may only contain letters, digits, ., /, +, " +
-				"-, _");
-		}
-
-		if(name.startsWith("__") && name.endsWith("__"))
-			throw new IllegalArgumentException("parameter name " +
-				"may not start and end with a double " +
-				"underscore");
-
-		if(die_on_bad_params && !__template__.contains(name))
-			throw new IllegalArgumentException(name + 
-				"is not a valid template entity");
-
-		if(value==null)
-			value="";
-
-		String type = value.getClass().getName();
-		if(type.indexOf(".") > 0)
-			type = type.substring(type.lastIndexOf(".")+1);
-
-		String valid_types = ",String,Vector,Boolean,Integer,Template";
-
-		if(valid_types.indexOf(type) < 0)
-			throw new ClassCastException(
-					"value is neither scalar nor list nor Template");
-
-		name=case_sensitive?name:name.toLowerCase();
-
-		if(!case_sensitive && type.equals("Vector")) {
-			value = lowerCaseAll((Vector)value);
-		}
-			
-		Util.debug_print("setting: " + name);
-		params.put(name, value);
-
-		dirty=true;
-		return value;
-	}
-
-	private static Vector lowerCaseAll(Vector v)
-	{
-		Vector v2 = new Vector();
-		for(Enumeration e = v.elements(); e.hasMoreElements(); ) {
-			Hashtable h = (Hashtable)e.nextElement();
-			if(h == null) {
-				v2.addElement(h);
-				continue;
-			}
-			Hashtable h2 = new Hashtable();
-			for(Enumeration e2 = h.keys(); e2.hasMoreElements(); ) {
-				String key = (String)e2.nextElement();
-				Object value = h.get(key);
-				String value_type = value.getClass().getName();
-				Util.debug_print("to lower case: " + key + "(" + value_type + ")");
-				if(value_type.endsWith(".Vector"))
-					value = lowerCaseAll((Vector)value);
-				h2.put(key.toLowerCase(), value);
-			}
-			v2.addElement(h2);
-		}
-		return v2;
-	}
-
-	private static boolean boolify(Object o)
-	{
-		String s;
-		if(o.getClass().getName().endsWith(".Boolean"))
-			return ((Boolean)o).booleanValue();
-		else if(o.getClass().getName().endsWith(".String"))
-			s = (String)o;
-		else
-			s = o.toString();
-
-		if(s.equals("0") || s.equals("") || s.equals("false"))
-			return false;
-		return true;
-	}
-
-	private static int intify(Object o)
-	{
-		String s;
-		if(o.getClass().getName().endsWith(".Integer"))
-			return ((Integer)o).intValue();
-		else if(o.getClass().getName().endsWith(".String"))
-			s = (String)o;
-		else
-			s = o.toString();
-
-		try {
-			return Integer.parseInt(s);
-		} catch(NumberFormatException nfe) {
-			return 0;
-		}
-	}
-
-	private static String stringify(boolean b)
-	{
-		if(b)
-			return "1";
-		else
-			return "";
-	}
-	
-	private BufferedReader openFile(String filename)
-			throws FileNotFoundException
-	{
-		boolean add_path=true;
-
-		if(!elements.empty() && !search_path_on_include)
-			add_path=false;
-
-		if(filename.startsWith("/"))
-			add_path=false;
-
-		if(this.path == null)
-			add_path=false;
-
-		Util.debug_print("open " + filename);
-		if(!add_path)
-			return new BufferedReader(new FileReader(filename));
-
-		BufferedReader br=null;
-		
-		for(int i=0; i<this.path.length; i++) {
-			try {
-				Util.debug_print("trying " + this.path[i] +
-						"/" + filename);
-				br = new BufferedReader(
-					new FileReader(
-						this.path[i] + "/" + filename
-					)
-				);
-				break;
-			} catch (FileNotFoundException fnfe) {
-			}
-		}
-
-		if(br == null)
-			throw new FileNotFoundException(filename);
-
-		return br;
-	}
-}
diff --git a/apps/q/java/src/HTML/Tmpl/Element/Conditional.java b/apps/q/java/src/HTML/Tmpl/Element/Conditional.java
deleted file mode 100644
index 25a8d4e6d231909842c7e9a4803ed53850477eb6..0000000000000000000000000000000000000000
--- a/apps/q/java/src/HTML/Tmpl/Element/Conditional.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
-*      HTML.Template:  A module for using HTML Templates with java
-*
-*      Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
-*
-*      This module is free software; you can redistribute it
-*      and/or modify it under the terms of either:
-*
-*      a) the GNU General Public License as published by the Free
-*      Software Foundation; either version 1, or (at your option)
-*      any later version, or
-*
-*      b) the "Artistic License" which comes with this module.
-*
-*      This program is distributed in the hope that it will be
-*      useful, but WITHOUT ANY WARRANTY; without even the implied
-*      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-*      PURPOSE.  See either the GNU General Public License or the
-*      Artistic License for more details.
-*
-*      You should have received a copy of the Artistic License
-*      with this module, in the file ARTISTIC.  If not, I'll be
-*      glad to provide one.
-*
-*      You should have received a copy of the GNU General Public
-*      License along with this program; if not, write to the Free
-*      Software Foundation, Inc., 59 Temple Place, Suite 330,
-*      Boston, MA 02111-1307 USA
-*
-* Modified by David McNab (david@rebirthing.co.nz) to allow nesting of
-* templates (ie, passing a child Template object as a value argument
-* to a .setParam() invocation on a parent Template object).
-*
-*/
-
-package HTML.Tmpl.Element;
-
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.NoSuchElementException;
-import java.util.Vector;
-
-import HTML.Template;
-
-public class Conditional extends Element
-{
-	private boolean control_val = false;
-	private Vector [] data;
-
-	public Conditional(String type, String name) 
-			throws IllegalArgumentException
-	{
-		if(type.equalsIgnoreCase("if"))
-			this.type="if";
-		else if(type.equalsIgnoreCase("unless"))
-			this.type="unless";
-		else
-			throw new IllegalArgumentException(
-				"Unrecognised type: " + type);
-
-		this.name = name;
-		this.data = new Vector[2];
-		this.data[0] = new Vector();
-	}
-
-	public void addBranch() throws IndexOutOfBoundsException
-	{
-		if(data[1] != null)
-			throw new IndexOutOfBoundsException("Already have two branches");
-
-		if(data[0] == null)
-			data[0] = new Vector();
-		else if(data[1] == null)
-			data[1] = new Vector();
-	}
-
-	public void add(String text)
-	{
-		if(data[1] != null)
-			data[1].addElement(text);
-		else
-			data[0].addElement(text);
-	}
-
-	public void add(Element node)
-	{
-		if(data[1] != null)
-			data[1].addElement(node);
-		else
-			data[0].addElement(node);
-	}
-
-	public void setControlValue(Object control_val)
-			throws IllegalArgumentException
-	{
-		this.control_val = process_var(control_val);
-	}
-
-	public String parse(Hashtable params)
-	{
-		if(!params.containsKey(this.name))
-			this.control_val = false;
-		else	
-			setControlValue(params.get(this.name));
-
-		StringBuffer output = new StringBuffer();
-
-		Enumeration de;
-		if(type.equals("if") && control_val ||
-			type.equals("unless") && !control_val)
-			de = data[0].elements();
-		else if(data[1] != null)
-			de = data[1].elements();
-		else
-			return "";
-
-		while(de.hasMoreElements()) {
-			Object e = de.nextElement();
-            String eType = e.getClass().getName();
-			if(eType.endsWith(".String"))
-				output.append((String)e);
-            else if (eType.endsWith(".Template"))
-                output.append(((Template)e).output());
-			else
-				output.append(((Element)e).parse(params));
-		}
-
-		return output.toString();
-	}
-
-	public String typeOfParam(String param)
-			throws NoSuchElementException
-	{
-		for(int i=0; i<data.length; i++)
-		{
-			if(data[i] == null)
-				continue;
-			for(Enumeration e = data[i].elements(); 
-				e.hasMoreElements();)
-			{
-				Object o = e.nextElement();
-				if(o.getClass().getName().endsWith(".String"))
-					continue;
-				if(((Element)o).Name().equals(param))
-					return ((Element)o).Type();
-			}
-		}
-		throw new NoSuchElementException(param);
-	}
-
-	private boolean process_var(Object control_val) 
-			throws IllegalArgumentException 
-	{
-		String control_class = "";
-
-		if(control_val == null)
-			return false;
-		
-		control_class=control_val.getClass().getName();
-		if(control_class.indexOf(".") > 0)
-			control_class = control_class.substring(
-					control_class.lastIndexOf(".")+1);
-
-		if(control_class.equals("String")) {
-			return !(((String)control_val).equals("") || 
-				((String)control_val).equals("0"));
-		} else if(control_class.equals("Vector")) {
-			return !((Vector)control_val).isEmpty();
-		} else if(control_class.equals("Boolean")) {
-			return ((Boolean)control_val).booleanValue();
-		} else if(control_class.equals("Integer")) {
-			return (((Integer)control_val).intValue() != 0);
-		} else {
-			throw new IllegalArgumentException("Unrecognised type");
-		}
-	}
-}
-
diff --git a/apps/q/java/src/HTML/Tmpl/Element/Element.java b/apps/q/java/src/HTML/Tmpl/Element/Element.java
deleted file mode 100644
index 2dea977fa437d922062e67e02da7123f2a4b9928..0000000000000000000000000000000000000000
--- a/apps/q/java/src/HTML/Tmpl/Element/Element.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-*      HTML.Template:  A module for using HTML Templates with java
-*
-*      Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
-*
-*      This module is free software; you can redistribute it
-*      and/or modify it under the terms of either:
-*
-*      a) the GNU General Public License as published by the Free
-*      Software Foundation; either version 1, or (at your option)
-*      any later version, or
-*
-*      b) the "Artistic License" which comes with this module.
-*
-*      This program is distributed in the hope that it will be
-*      useful, but WITHOUT ANY WARRANTY; without even the implied
-*      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-*      PURPOSE.  See either the GNU General Public License or the
-*      Artistic License for more details.
-*
-*      You should have received a copy of the Artistic License
-*      with this module, in the file ARTISTIC.  If not, I'll be
-*      glad to provide one.
-*
-*      You should have received a copy of the GNU General Public
-*      License along with this program; if not, write to the Free
-*      Software Foundation, Inc., 59 Temple Place, Suite 330,
-*      Boston, MA 02111-1307 USA
-*/
-
-
-package HTML.Tmpl.Element;
-import java.util.Hashtable;
-import java.util.NoSuchElementException;
-
-public abstract class Element
-{
-	protected String type;
-	protected String name="";
-
-	public abstract String parse(Hashtable params);
-	public abstract String typeOfParam(String param)
-			throws NoSuchElementException;
-
-	public void add(String data){}
-	public void add(Element node){}
-
-	public boolean contains(String param)
-	{
-		try {
-			return (typeOfParam(param) != null?true:false);
-		} catch(NoSuchElementException nse) {
-			return false;
-		}
-	}
-
-	public final String Type()
-	{
-		return type;
-	}
-
-	public final String Name()
-	{
-		return name;
-	}
-}
diff --git a/apps/q/java/src/HTML/Tmpl/Element/If.java b/apps/q/java/src/HTML/Tmpl/Element/If.java
deleted file mode 100644
index 4384e8fbdeb88c568ff69d65270972135282bd88..0000000000000000000000000000000000000000
--- a/apps/q/java/src/HTML/Tmpl/Element/If.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-*      HTML.Template:  A module for using HTML Templates with java
-*
-*      Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
-*
-*      This module is free software; you can redistribute it
-*      and/or modify it under the terms of either:
-*
-*      a) the GNU General Public License as published by the Free
-*      Software Foundation; either version 1, or (at your option)
-*      any later version, or
-*
-*      b) the "Artistic License" which comes with this module.
-*
-*      This program is distributed in the hope that it will be
-*      useful, but WITHOUT ANY WARRANTY; without even the implied
-*      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-*      PURPOSE.  See either the GNU General Public License or the
-*      Artistic License for more details.
-*
-*      You should have received a copy of the Artistic License
-*      with this module, in the file ARTISTIC.  If not, I'll be
-*      glad to provide one.
-*
-*      You should have received a copy of the GNU General Public
-*      License along with this program; if not, write to the Free
-*      Software Foundation, Inc., 59 Temple Place, Suite 330,
-*      Boston, MA 02111-1307 USA
-*/
-
-package HTML.Tmpl.Element;
-
-public class If extends Conditional
-{
-	public If(String control_var) throws IllegalArgumentException
-	{
-		super("if", control_var);
-	}
-}
diff --git a/apps/q/java/src/HTML/Tmpl/Element/Loop.java b/apps/q/java/src/HTML/Tmpl/Element/Loop.java
deleted file mode 100644
index cb1911a870550aea188a8cc832fd167e1901599d..0000000000000000000000000000000000000000
--- a/apps/q/java/src/HTML/Tmpl/Element/Loop.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
-*      HTML.Template:  A module for using HTML Templates with java
-*
-*      Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
-*
-*      This module is free software; you can redistribute it
-*      and/or modify it under the terms of either:
-*
-*      a) the GNU General Public License as published by the Free
-*      Software Foundation; either version 1, or (at your option)
-*      any later version, or
-*
-*      b) the "Artistic License" which comes with this module.
-*
-*      This program is distributed in the hope that it will be
-*      useful, but WITHOUT ANY WARRANTY; without even the implied
-*      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-*      PURPOSE.  See either the GNU General Public License or the
-*      Artistic License for more details.
-*
-*      You should have received a copy of the Artistic License
-*      with this module, in the file ARTISTIC.  If not, I'll be
-*      glad to provide one.
-*
-*      You should have received a copy of the GNU General Public
-*      License along with this program; if not, write to the Free
-*      Software Foundation, Inc., 59 Temple Place, Suite 330,
-*      Boston, MA 02111-1307 USA
-*/
-
-package HTML.Tmpl.Element;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.NoSuchElementException;
-import java.util.Vector;
-
-public class Loop extends Element
-{
-	private boolean loop_context_vars=false;
-	private boolean global_vars=false;
-
-	private Vector control_val = null;
-	private Vector data;
-
-	public Loop(String name)
-	{
-		this.type = "loop";
-		this.name = name;
-		this.data = new Vector();
-	}
-
-	public Loop(String name, boolean loop_context_vars)
-	{
-		this(name);
-		this.loop_context_vars=loop_context_vars;
-	}
-
-	public Loop(String name, boolean loop_context_vars, boolean global_vars)
-	{
-		this(name);
-		this.loop_context_vars=loop_context_vars;
-		this.global_vars=global_vars;
-	}
-
-	public void add(String text)
-	{
-		data.addElement(text);
-	}
-
-	public void add(Element node)
-	{
-		data.addElement(node);
-	}
-
-	public void setControlValue(Vector control_val)
-			throws IllegalArgumentException
-	{
-		this.control_val = process_var(control_val);
-	}
-
-	public String parse(Hashtable p)
-	{
-		if(!p.containsKey(this.name))
-			this.control_val = null;
-		else {
-			Object o = p.get(this.name);
-			if(!o.getClass().getName().endsWith(".Vector") &&
-					!o.getClass().getName().endsWith(".List"))
-				throw new ClassCastException(
-					"Attempt to set <tmpl_loop> with a non-list.  tmpl_loop=" + this.name);
-			setControlValue((Vector)p.get(this.name));
-		}
-
-		if(control_val == null)
-			return "";
-
-		StringBuffer output = new StringBuffer();
-		Enumeration iterator = control_val.elements();
-
-		boolean first=true;
-		boolean last=false;
-		boolean inner=false;
-		boolean odd=true;
-		int counter=1;
-
-		while(iterator.hasMoreElements()) {
-			Hashtable params = (Hashtable)iterator.nextElement();
-
-			if(params==null)
-				params = new Hashtable();
-
-			if(global_vars) {
-				for(Enumeration e = p.keys(); e.hasMoreElements();) {
-					Object key = e.nextElement();
-					if(!params.containsKey(key))
-						params.put(key, p.get(key));
-				}
-			}
-
-			if(loop_context_vars) {
-				if(!iterator.hasMoreElements())
-					last=true;
-				inner = !first && !last;
-
-				params.put("__FIRST__", first?"1":"");
-				params.put("__LAST__", last?"1":"");
-				params.put("__ODD__", odd?"1":"");
-				params.put("__INNER__", inner?"1":"");
-				params.put("__COUNTER__", "" + (counter++));
-			}
-				
-			Enumeration de = data.elements();
-			while(de.hasMoreElements()) {
-
-				Object e = de.nextElement();
-				if(e.getClass().getName().indexOf("String")>-1)
-					output.append((String)e);
-				else
-					output.append(((Element)e).parse(params));
-			}
-			first = false;
-			odd = !odd;
-		}
-
-		return output.toString();
-	}
-
-	public String typeOfParam(String param)
-			throws NoSuchElementException
-	{
-		for(Enumeration e = data.elements(); e.hasMoreElements();)
-		{
-			Object o = e.nextElement();
-			if(o.getClass().getName().endsWith(".String"))
-				continue;
-			if(((Element)o).Name().equals(param))
-				return ((Element)o).Type();
-		}
-		throw new NoSuchElementException(param);
-	}
-
-	private Vector process_var(Vector control_val) 
-			throws IllegalArgumentException 
-	{
-		String control_class = "";
-
-		if(control_val == null)
-			return null;
-		
-		control_class=control_val.getClass().getName();
-
-		if(control_class.indexOf("Vector") > -1) {
-			if(control_val.isEmpty())
-				return null;
-		} else {
-			throw new IllegalArgumentException("Unrecognised type");
-		}
-
-		return control_val;
-	}
-
-}
-
diff --git a/apps/q/java/src/HTML/Tmpl/Element/Unless.java b/apps/q/java/src/HTML/Tmpl/Element/Unless.java
deleted file mode 100644
index 8caca00c690322972b8d795d6cf58c6c15482887..0000000000000000000000000000000000000000
--- a/apps/q/java/src/HTML/Tmpl/Element/Unless.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-*      HTML.Template:  A module for using HTML Templates with java
-*
-*      Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
-*
-*      This module is free software; you can redistribute it
-*      and/or modify it under the terms of either:
-*
-*      a) the GNU General Public License as published by the Free
-*      Software Foundation; either version 1, or (at your option)
-*      any later version, or
-*
-*      b) the "Artistic License" which comes with this module.
-*
-*      This program is distributed in the hope that it will be
-*      useful, but WITHOUT ANY WARRANTY; without even the implied
-*      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-*      PURPOSE.  See either the GNU General Public License or the
-*      Artistic License for more details.
-*
-*      You should have received a copy of the Artistic License
-*      with this module, in the file ARTISTIC.  If not, I'll be
-*      glad to provide one.
-*
-*      You should have received a copy of the GNU General Public
-*      License along with this program; if not, write to the Free
-*      Software Foundation, Inc., 59 Temple Place, Suite 330,
-*      Boston, MA 02111-1307 USA
-*/
-
-package HTML.Tmpl.Element;
-
-public class Unless extends Conditional
-{
-	public Unless(String control_var) throws IllegalArgumentException
-	{
-		super("unless", control_var);
-	}
-}
diff --git a/apps/q/java/src/HTML/Tmpl/Element/Var.java b/apps/q/java/src/HTML/Tmpl/Element/Var.java
deleted file mode 100644
index bf761b9c0a9e32c8e8af9effdbdf6a565c063fb5..0000000000000000000000000000000000000000
--- a/apps/q/java/src/HTML/Tmpl/Element/Var.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
-*      HTML.Template:  A module for using HTML Templates with java
-*
-*      Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
-*
-*      This module is free software; you can redistribute it
-*      and/or modify it under the terms of either:
-*
-*      a) the GNU General Public License as published by the Free
-*      Software Foundation; either version 1, or (at your option)
-*      any later version, or
-*
-*      b) the "Artistic License" which comes with this module.
-*
-*      This program is distributed in the hope that it will be
-*      useful, but WITHOUT ANY WARRANTY; without even the implied
-*      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-*      PURPOSE.  See either the GNU General Public License or the
-*      Artistic License for more details.
-*
-*      You should have received a copy of the Artistic License
-*      with this module, in the file ARTISTIC.  If not, I'll be
-*      glad to provide one.
-*
-*      You should have received a copy of the GNU General Public
-*      License along with this program; if not, write to the Free
-*      Software Foundation, Inc., 59 Temple Place, Suite 330,
-*      Boston, MA 02111-1307 USA
-*
-* Modified by David McNab (david@rebirthing.co.nz) to allow nesting of
-* templates (ie, passing a child Template object as a value argument
-* to a .setParam() invocation on a parent Template object).
-*/
-
-package HTML.Tmpl.Element;
-import java.util.Hashtable;
-import java.util.NoSuchElementException;
-
-import HTML.Template;
-import HTML.Tmpl.Util;
-
-public class Var extends Element
-{
-	public static final int ESCAPE_NONE  = 0;
-	public static final int ESCAPE_URL   = 1;
-	public static final int ESCAPE_HTML  = 2;
-	public static final int ESCAPE_QUOTE = 4;
-	
-	public Var(String name, int escape, Object default_value)
-			throws IllegalArgumentException
-	{
-		this(name, escape);
-		this.default_value = stringify(default_value);
-	}
-
-	public Var(String name, int escape) 
-			throws IllegalArgumentException
-	{
-		if(name == null)
-			throw new IllegalArgumentException("tmpl_var must have a name");
-		this.type = "var";
-		this.name = name;
-		this.escape = escape;
-	}
-
-	public Var(String name, String escape)
-			throws IllegalArgumentException
-	{
-		this(name, escape, null);
-	}
-
-	public Var(String name, String escape, Object default_value) 
-			throws IllegalArgumentException
-	{
-		this(name, ESCAPE_NONE, default_value);
-
-		if(escape.equalsIgnoreCase("html"))
-			this.escape = ESCAPE_HTML;
-		else if(escape.equalsIgnoreCase("url"))
-			this.escape = ESCAPE_URL;
-		else if(escape.equalsIgnoreCase("quote"))
-			this.escape = ESCAPE_QUOTE;
-	}
-
-	public Var(String name, boolean escape) 
-			throws IllegalArgumentException
-	{
-		this(name, escape?ESCAPE_HTML:ESCAPE_NONE);
-	}
-
-	public String parse(Hashtable params)
-	{
-		String value = null;
-
-		if(params.containsKey(this.name))
-			value = stringify(params.get(this.name));
-		else
-			value = this.default_value;
-
-		if(value == null)
-			return "";
-
-		if(this.escape == ESCAPE_HTML)
-			return Util.escapeHTML(value);
-		else if(this.escape == ESCAPE_URL)
-			return Util.escapeURL(value);
-		else if(this.escape == ESCAPE_QUOTE)
-			return Util.escapeQuote(value);
-		else
-			return value;
-	}
-
-	public String typeOfParam(String param)
-			throws NoSuchElementException
-	{
-		throw new NoSuchElementException(param);
-	}
-
-	private String stringify(Object o)
-	{
-		if(o == null)
-			return null;
-
-		String cname = o.getClass().getName();
-		if(cname.endsWith(".String"))
-			return (String)o;
-		else if(cname.endsWith(".Integer"))
-			return ((Integer)o).toString();
-		else if(cname.endsWith(".Boolean"))
-			return ((Boolean)o).toString();
-		else if(cname.endsWith(".Date"))
-			return ((java.util.Date)o).toString();
-		else if(cname.endsWith(".Vector"))
-			throw new ClassCastException("Attempt to set <tmpl_var> with a non-scalar. Var name=" + this.name);
-        else if(cname.endsWith(".Template"))
-            return ((Template)o).output();
-		else
-			throw new ClassCastException("Unknown object type: " + cname);
-	}
-
-	// Private data starts here
-	private int escape=ESCAPE_NONE;
-	private String default_value=null;
-
-}
diff --git a/apps/q/java/src/HTML/Tmpl/Filter.java b/apps/q/java/src/HTML/Tmpl/Filter.java
deleted file mode 100644
index 5d5f8211259a950869784759a3e0a1472587a72c..0000000000000000000000000000000000000000
--- a/apps/q/java/src/HTML/Tmpl/Filter.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
-*      HTML.Template:  A module for using HTML Templates with java
-*
-*      Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
-*
-*      This module is free software; you can redistribute it
-*      and/or modify it under the terms of either:
-*
-*      a) the GNU General Public License as published by the Free
-*      Software Foundation; either version 1, or (at your option)
-*      any later version, or
-*
-*      b) the "Artistic License" which comes with this module.
-*
-*      This program is distributed in the hope that it will be
-*      useful, but WITHOUT ANY WARRANTY; without even the implied
-*      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-*      PURPOSE.  See either the GNU General Public License or the
-*      Artistic License for more details.
-*
-*      You should have received a copy of the Artistic License
-*      with this module, in the file ARTISTIC.  If not, I'll be
-*      glad to provide one.
-*
-*      You should have received a copy of the GNU General Public
-*      License along with this program; if not, write to the Free
-*      Software Foundation, Inc., 59 Temple Place, Suite 330,
-*      Boston, MA 02111-1307 USA
-*/
-
-
-package HTML.Tmpl;
-
-/**
- * Pre-parse filters for HTML.Template templates.
- * <p>
- * The HTML.Tmpl.Filter interface allows you to write Filters
- * for your templates.  The filter is called after the template
- * is read and before it is parsed.
- * <p>
- * You can use a filter to make changes in the template file before
- * it is parsed by HTML.Template, so for example, use it to replace
- * constants, or to translate your own tags to HTML.Template tags.
- * <p>
- * A common usage would be to do what you think you're doing when you
- * do <code>&lt;TMPL_INCLUDE file="&lt;TMPL_VAR name="the_file"&gt;"&gt;</code>:
- * <p>
- * myTemplate.tmpl:
- * <pre>
- *	&lt;TMPL_INCLUDE file="&lt;%the_file%&gt;"&gt;
- * </pre>
- * <p>
- * myFilter.java:
- * <pre>
- *	class myFilter implements HTML.Tmpl.Filter
- *	{
- *		private String myFile;
- *		private int type=SCALAR
- *
- *		public myFilter(String myFile) {
- *			this.myFile = myFile;
- *		}
- *
- *		public int format() {
- *			return this.type;
- *		}
- *
- *		public String parse(String t) {
- *			// replace all &lt;%the_file%&gt; with myFile
- *			return t;
- *		}
- *
- *		public String [] parse(String [] t) {
- *			throw new UnsupportedOperationException();
- *		}
- *	}
- * </pre>
- * <p>
- * myClass.java:
- * <pre>
- *	Hashtable params = new Hashtable();
- *	params.put("filename", "myTemplate.tmpl");
- *	params.put("filter", new myFilter("myFile.tmpl"));
- *	Template t = new Template(params);
- * </pre>
- *
- * @author	Philip S Tellis
- * @version	0.0.1
- */
-public interface Filter
-{
-	/**
-	 * Tells HTML.Template to call the parse(String) method of this filter.
-	 */
-	public final static int SCALAR=1;
-
-	/**
-	 * Tells HTML.Template to call the parse(String []) method of this 
-	 * filter.
-	 */
-	public final static int ARRAY=2;
-
-	/**
-	 * Tells HTML.Template what kind of filter this is.
-	 * Should return either SCALAR or ARRAY to indicate which parse method
-	 * must be called.
-	 *
-	 * @return	the values SCALAR or ARRAY indicating which parse method
-	 *		is to be called
-	 */
-	public int format();
-
-	/**
-	 * parses the template as a single string, and returns the parsed 
-	 * template as a single string.
-	 * <p>
-	 * Should throw an UnsupportedOperationException if it isn't implemented
-	 *
-	 * @param t	a string containing the entire template
-	 *
-	 * @return	a string containing the template after you've parsed it
-	 *
-	 * @throws UnsupportedOperationException	if this method isn't 
-	 *						implemented
-	 */
-	public String parse(String t);
-
-	/**
-	 * parses the template as an array of strings, and returns the parsed 
-	 * template as an array of strings.
-	 * <p>
-	 * Should throw an UnsupportedOperationException if it isn't implemented
-	 *
-	 * @param t	an array of strings containing the template - one line 
-	 *		at a time
-	 *
-	 * @return	an array of strings containing the parsed template - 
-	 *		one line at a time
-	 *
-	 * @throws UnsupportedOperationException	if this method isn't 
-	 *						implemented
-	 */
-	public String [] parse(String [] t);
-}
-
diff --git a/apps/q/java/src/HTML/Tmpl/Parsers/Parser.java b/apps/q/java/src/HTML/Tmpl/Parsers/Parser.java
deleted file mode 100644
index 78b9ceff91aedc579f57f56d722ca822e334527e..0000000000000000000000000000000000000000
--- a/apps/q/java/src/HTML/Tmpl/Parsers/Parser.java
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
-*      HTML.Template:  A module for using HTML Templates with java
-*
-*      Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
-*
-*      This module is free software; you can redistribute it
-*      and/or modify it under the terms of either:
-*
-*      a) the GNU General Public License as published by the Free
-*      Software Foundation; either version 1, or (at your option)
-*      any later version, or
-*
-*      b) the "Artistic License" which comes with this module.
-*
-*      This program is distributed in the hope that it will be
-*      useful, but WITHOUT ANY WARRANTY; without even the implied
-*      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-*      PURPOSE.  See either the GNU General Public License or the
-*      Artistic License for more details.
-*
-*      You should have received a copy of the Artistic License
-*      with this module, in the file ARTISTIC.  If not, I'll be
-*      glad to provide one.
-*
-*      You should have received a copy of the GNU General Public
-*      License along with this program; if not, write to the Free
-*      Software Foundation, Inc., 59 Temple Place, Suite 330,
-*      Boston, MA 02111-1307 USA
-*/
-
-
-package HTML.Tmpl.Parsers;
-import java.util.NoSuchElementException;
-import java.util.Properties;
-import java.util.StringTokenizer;
-import java.util.Vector;
-
-import HTML.Tmpl.Util;
-import HTML.Tmpl.Element.Element;
-import HTML.Tmpl.Element.If;
-import HTML.Tmpl.Element.Loop;
-import HTML.Tmpl.Element.Unless;
-
-public class Parser
-{
-	private boolean case_sensitive=false;
-	private boolean strict=true;
-	private boolean loop_context_vars=false;
-	private boolean global_vars=false;
-
-	public Parser()
-	{
-	}
-
-	public Parser(String [] args)
-			throws ArrayIndexOutOfBoundsException,
-				IllegalArgumentException
-	{
-		if(args.length%2 != 0)
-			throw new ArrayIndexOutOfBoundsException("odd number of arguments passed");
-
-		for(int i=0; i<args.length; i+=2) {
-			if(args[i].equals("case_sensitive")) {
-				String cs = args[i+1];
-				if(cs.equals("") || cs.equals("0"))
-					this.case_sensitive=false;
-				else
-					this.case_sensitive=true;
-			} else if(args[i].equals("strict")) {
-				String s = args[i+1];
-				if(s.equals("") || s.equals("0"))
-					this.strict=false;
-				else
-					this.strict=true;
-			} else if(args[i].equals("loop_context_vars")) {
-				String s = args[i+1];
-				if(s.equals("") || s.equals("0"))
-					this.loop_context_vars=false;
-				else
-					this.loop_context_vars=true;
-
-			} else if(args[i].equals("global_vars")) {
-				String s = args[i+1];
-				if(s.equals("") || s.equals("0"))
-					this.global_vars=false;
-				else
-					this.global_vars=true;
-			} else {
-				throw new IllegalArgumentException(args[i]);
-			}
-		}
-	}
-
-	public Element getElement(Properties p)
-			throws NoSuchElementException
-	{
-		String type = p.getProperty("type");
-
-		if(type.equals("if"))
-			return new If(p.getProperty("name"));
-		else if(type.equals("unless"))
-			return new Unless(p.getProperty("name"));
-		else if(type.equals("loop"))
-			return new Loop(p.getProperty("name"), 
-						loop_context_vars, global_vars);
-		else
-			throw new NoSuchElementException(type);
-	}
-
-	public Vector parseLine(String line)
-			throws IllegalArgumentException
-	{
-		int pos=0, endpos;
-		Vector parts = new Vector();
-
-		char [] c = line.toCharArray();
-		int i=0;
-
-		StringBuffer temp = new StringBuffer();
-
-		for(i=0; i<c.length; i++) {
-			if(c[i] != '<') {
-				temp.append(c[i]);
-			} else {
-				// found a tag
-				Util.debug_print("line so far: " + temp);
-				StringBuffer tag = new StringBuffer();
-				for(; i<c.length && c[i] != '>';  i++) {
-					tag.append(c[i]);
-				}
-				// > is not allowed inside a template tag
-				// so we can be sure that if this is a 
-				// template tag, it ends with a >
-
-				// add the closing > as well
-				if(i<c.length)
-					tag.append(c[i]);
-
-				// if this contains more < inside it,
-				// then it could possibly be a template
-				// tag inside a html tag
-				// so remove external tag parts
-
-				while(tag.toString().substring(1).indexOf("<") 
-						> -1)
-				{
-					do {
-						temp.append(tag.charAt(0));
-						tag=new StringBuffer(
-							tag.toString().substring(1));
-					} while(tag.charAt(0) != '<');
-				}
-
-				Util.debug_print("tag: " + tag);
-
-				String test_tag = tag.toString().toLowerCase();
-				// if it doesn't contain tmpl_ it is not
-				// a template tag
-				if(test_tag.indexOf("tmpl_") < 0) {
-					temp.append(tag);
-					continue;
-				}
-
-				// may be a template tag
-				// check if it starts with tmpl_
-				
-				test_tag = cleanTag(test_tag);
-
-				Util.debug_print("clean: " + test_tag);
-
-				// check if it is a closing tag
-				if(test_tag.startsWith("/"))
-					test_tag = test_tag.substring(1);
-
-				// if it still doesn't start with tmpl_
-				// then it is not a template tag
-				if(!test_tag.startsWith("tmpl_")) {
-					temp.append(tag);
-					continue;
-				}
-
-				// now it must be a template tag
-				String tag_type=getTagType(test_tag);
-
-				if(tag_type == null) {
-					if(strict)
-						throw new 
-						IllegalArgumentException(
-							tag.toString());
-					else
-						temp.append(tag);
-				}
-
-				Util.debug_print("type: " + tag_type);
-
-				// if this was an invalid key and we've
-				// reached so far, then next iteration
-				if(tag_type == null)
-					continue;
-				
-				// now, push the previous stuff
-				// into the Vector
-				if(temp.length()>0) {
-					parts.addElement(temp.toString());
-					temp = new StringBuffer();
-				}
-
-				// it is a valid template tag
-				// get its properties
-
-				Util.debug_print("Checking: " + tag);
-				Properties tag_props = 
-					getTagProps(tag.toString());
-
-				if(tag_props.containsKey("name"))
-					Util.debug_print("name: " + 
-						tag_props.getProperty("name"));
-				else
-					Util.debug_print("no name");
-
-				parts.addElement(tag_props);
-			}
-		}
-
-		if(temp.length()>0)
-			parts.addElement(temp.toString());
-
-		return parts;
-	}
-
-	private String cleanTag(String tag)
-			throws IllegalArgumentException
-	{
-		String test_tag = new String(tag);
-		// first remove < and >
-		if(test_tag.startsWith("<"))
-			test_tag = test_tag.substring(1);
-		if(test_tag.endsWith(">"))
-			test_tag = test_tag.substring(0, test_tag.length()-1);
-		else
-			throw new IllegalArgumentException("Tags must start " +
-					"and end on the same line");
-
-		// remove any leading !-- and trailing
-		// -- in case of comment style tags
-		if(test_tag.startsWith("!--")) {
-			test_tag=test_tag.substring(3);
-		}
-		if(test_tag.endsWith("--")) {
-			test_tag=test_tag.substring(0, test_tag.length()-2);
-		}
-		// then leading and trailing spaces
-		test_tag = test_tag.trim();
-
-		return test_tag;
-	}
-
-	private String getTagType(String tag)
-	{
-		int sp = tag.indexOf(" ");
-		String tag_type="";
-		if(sp < 0) {
-			tag_type = tag.toLowerCase();
-		} else {
-			tag_type = tag.substring(0, sp).toLowerCase();
-		}
-		if(tag_type.startsWith("tmpl_"))
-			tag_type=tag_type.substring(5);
-
-		Util.debug_print("tag_type: " + tag_type);
-
-		if(tag_type.equals("var") ||
-				tag_type.equals("if") ||
-				tag_type.equals("unless") ||
-				tag_type.equals("loop") ||
-				tag_type.equals("include") ||
-				tag_type.equals("else")) {
-			return tag_type;
-		} else {
-			return null;
-		}
-	}
-
-	private Properties getTagProps(String tag)
-			throws IllegalArgumentException,
-				NullPointerException
-	{
-		Properties p = new Properties();
-
-		tag = cleanTag(tag);
-
-		Util.debug_print("clean: " + tag);
-		
-		if(tag.startsWith("/")) {
-			p.put("close", "true");
-			tag=tag.substring(1);
-		} else {
-			p.put("close", "");
-		}
-
-		Util.debug_print("close: " + p.getProperty("close"));
-		
-		p.put("type", getTagType(tag));
-
-		Util.debug_print("type: " + p.getProperty("type"));
-
-		if(p.getProperty("type").equals("else") ||
-				p.getProperty("close").equals("true"))
-			return p;
-
-		if(p.getProperty("type").equals("var"))
-			p.put("escape", "");
-
-		int sp = tag.indexOf(" ");
-		// if we've got so far, this must succeed
-
-		tag = tag.substring(sp).trim();
-		Util.debug_print("checking params: " + tag);
-
-		// now, we should have either name=value pairs
-		// or name space escape in case of old style vars
-
-		if(tag.indexOf("=") < 0) {
-			// no = means old style
-			// first will be var name
-			// second if any will be escape
-
-			sp = tag.toLowerCase().indexOf(" escape");
-			if(sp < 0) {
-				// no escape
-				p.put("name", tag);
-				p.put("escape", "0");
-			} else {
-				tag = tag.substring(0, sp);
-				p.put("name", tag);
-				p.put("escape", "html");
-			}
-		} else {
-			// = means name=value pairs.
-			// use a StringTokenizer
-			StringTokenizer st = new StringTokenizer(tag, " =");
-			while(st.hasMoreTokens()) {
-				String key, value;
-				key = st.nextToken().toLowerCase();
-				if(st.hasMoreTokens())
-					value = st.nextToken();
-				else if(key.equals("escape"))
-					value = "html";
-				else
-					throw new NullPointerException(
-						"parameter " + key + " has no value");
-
-				if(value.startsWith("\"") && 
-						value.endsWith("\""))
-					value = value.substring(1,
-							value.length()-1);
-				else if(value.startsWith("'") && 
-						value.endsWith("'"))
-					value = value.substring(1,
-							value.length()-1);
-
-				if(value.length()==0)
-					throw new NullPointerException(
-						"parameter " + key + " has no value");
-
-				if(key.equals("escape"))
-					value=value.toLowerCase();
-
-				p.put(key, value);
-			}
-		}
-
-		String name = p.getProperty("name");
-		// if not case sensitive, and not special variable, flatten case
-		// never flatten case for includes
-		if(!case_sensitive && !p.getProperty("type").equals("include")
-			&& !( name.startsWith("__") && name.endsWith("__") ))
-		{
-			p.put("name", name.toLowerCase());
-		}
-
-		if(!Util.isNameChar(name))
-			throw new IllegalArgumentException(
-				"parameter name may only contain " +
-				"letters, digits, ., /, +, -, _");
-		// __var__ is allowed in the template, but not in the
-		// code.  this is so that people can reference __FIRST__,
-		// etc
-
-		return p;
-	}
-}
diff --git a/apps/q/java/src/HTML/Tmpl/Util.java b/apps/q/java/src/HTML/Tmpl/Util.java
deleted file mode 100644
index 46ad2568bb8b0da29ab8f0131cf2488b9622fb0c..0000000000000000000000000000000000000000
--- a/apps/q/java/src/HTML/Tmpl/Util.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
-*      HTML.Template:  A module for using HTML Templates with java
-*
-*      Copyright (c) 2002 Philip S Tellis (philip.tellis@iname.com)
-*
-*      This module is free software; you can redistribute it
-*      and/or modify it under the terms of either:
-*
-*      a) the GNU General Public License as published by the Free
-*      Software Foundation; either version 1, or (at your option)
-*      any later version, or
-*
-*      b) the "Artistic License" which comes with this module.
-*
-*      This program is distributed in the hope that it will be
-*      useful, but WITHOUT ANY WARRANTY; without even the implied
-*      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-*      PURPOSE.  See either the GNU General Public License or the
-*      Artistic License for more details.
-*
-*      You should have received a copy of the Artistic License
-*      with this module, in the file ARTISTIC.  If not, I'll be
-*      glad to provide one.
-*
-*      You should have received a copy of the GNU General Public
-*      License along with this program; if not, write to the Free
-*      Software Foundation, Inc., 59 Temple Place, Suite 330,
-*      Boston, MA 02111-1307 USA
-*/
-
-
-package HTML.Tmpl;
-
-public class Util
-{
-	public static boolean debug=false;
-
-	public static String escapeHTML(String element)
-	{
-		String s = new String(element);	// don't change the original
-		String [] metas = {"&", "<", ">", "\""};
-		String [] repls = {"&amp;", "&lt;", "&gt;", "&quot;"};
-		for(int i = 0; i < metas.length; i++) {
-			int pos=0;
-			do {
-				pos = s.indexOf(metas[i], pos);
-				if(pos<0)
-					break;
-
-				s = s.substring(0, pos) + repls[i] + s.substring(pos+1);
-				pos++;
-			} while(pos >= 0);
-		}
-
-		return s;
-	}
-
-	public static String escapeURL(String url)
-	{
-		StringBuffer s = new StringBuffer();
-		String no_escape = "./-_";
-
-		for(int i=0; i<url.length(); i++)
-		{
-			char c = url.charAt(i);
-			if(!Character.isLetterOrDigit(c) &&
-					no_escape.indexOf(c)<0) 
-			{
-				String h = Integer.toHexString((int)c);
-				s.append("%");
-				if(h.length()<2)
-					s.append("0");
-				s.append(h);
-			} else {
-				s.append(c);
-			}
-		}
-
-		return s.toString();
-	}
-
-	public static String escapeQuote(String element)
-	{
-		String s = new String(element);	// don't change the original
-		String [] metas = {"\"", "'"};
-		String [] repls = {"\\\"", "\\'"};
-		for(int i = 0; i < metas.length; i++) {
-			int pos=0;
-			do {
-				pos = s.indexOf(metas[i], pos);
-				if(pos<0)
-					break;
-
-				s = s.substring(0, pos) + repls[i] + s.substring(pos+1);
-				pos++;
-			} while(pos >= 0);
-		}
-
-		return s;
-	}
-
-	public static boolean isNameChar(char c)
-	{
-		return true;
-	}
-
-	public static boolean isNameChar(String s)
-	{
-		String alt_valid = "./+-_";
-
-		for(int i=0; i<s.length(); i++)
-			if(!Character.isLetterOrDigit(s.charAt(i)) &&
-					alt_valid.indexOf(s.charAt(i))<0)
-				return false;
-		return true;
-	}
-
-	public static void debug_print(String msg)
-	{
-		if(!debug)
-			return;
-
-		System.err.println(msg);
-	}
-
-	public static void debug_print(Object o)
-	{
-		debug_print(o.toString());
-	}
-}
diff --git a/apps/q/java/src/net/i2p/aum/AumUtil.java b/apps/q/java/src/net/i2p/aum/AumUtil.java
deleted file mode 100644
index b8932029574b9299f2364a2a622e298f98a2dcb2..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/AumUtil.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * AumUtil.java
- *
- * Created on March 24, 2005, 3:10 PM
- */
-
-package net.i2p.aum;
-
-/**
- *
- * @author  david
- */
-public class AumUtil {
-    
-    /** Creates a new instance of AumUtil */
-    public AumUtil() {
-    }
-
-}
diff --git a/apps/q/java/src/net/i2p/aum/DupHashtable.java b/apps/q/java/src/net/i2p/aum/DupHashtable.java
deleted file mode 100644
index 6184337f261f053193dfff31c7d3d21533467015..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/DupHashtable.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * NonUniqueProperties.java
- *
- * Created on April 9, 2005, 10:46 PM
- */
-
-package net.i2p.aum;
-
-import java.util.Hashtable;
-import java.util.Vector;
-
-/**
- * similar in some ways to Properties, except that duplicate keys
- * are allowed
- */
-public class DupHashtable extends Hashtable {
-    
-    /** Creates a new instance of NonUniqueProperties */
-    public DupHashtable() {
-        super();
-    }
-
-    /** Adds a value to be stored against key */
-    public void put(String key, String value) {
-
-        if (!containsKey(key)) {
-            put(key, new Vector());
-        }
-
-        ((Vector)get(key)).addElement(value);
-    }
-
-    /** retrieves a Vector of values for key, or empty vector if none */
-    public Vector get(String key) {
-        if (!containsKey(key)) {
-            return new Vector();
-        } else {
-            return (Vector)super.get(key);
-        }
-    }
-
-    /** returns the i-th value for given key, or dflt if key not found */
-    public String get(String key, int idx, String dflt) {
-        if (containsKey(key)) {
-            return get(key, idx);
-        } else {
-            return dflt;
-        }
-    }
-
-    /** returns the i-th value for given key
-     * @throws ArrayIndexOutOfBoundsException if idx is out of range
-     */
-    public String get(String key, int idx) {
-        return (String)((Vector)get(key)).get(idx);
-    }
-
-    /** returns the number of values for a given key */
-    public int numValuesFor(String key) {
-        if (!containsKey(key)) {
-            return 0;
-        } else {
-            return ((Vector)get(key)).size();
-        }
-    }
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/EchoClient.java b/apps/q/java/src/net/i2p/aum/EchoClient.java
deleted file mode 100644
index 9647084c1136f4cd00ed417d076cb8966e31c961..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/EchoClient.java
+++ /dev/null
@@ -1,161 +0,0 @@
-
-// a simple I2P stream client that makes connections to an EchoServer,
-// sends in stuff, and gets replies
-
-package net.i2p.aum;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.net.ConnectException;
-import java.net.NoRouteToHostException;
-
-import net.i2p.I2PException;
-import net.i2p.client.streaming.I2PSocket;
-import net.i2p.client.streaming.I2PSocketManager;
-import net.i2p.client.streaming.I2PSocketManagerFactory;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Destination;
-import net.i2p.util.Log;
-
-/**
- * a simple program which illustrates the use of I2P stream
- * sockets from a client point of view
- */
-
-public class EchoClient extends Thread
-{
-    public I2PSocketManager socketManager;
-    public I2PSocket clientSocket;
-    
-    public Destination dest;
-    
-    protected static Log _log;
-    
-    /**
-     * Creates an echoclient, given an I2P Destination object
-     */
-    public EchoClient(Destination remdest)
-    {
-        _log = new Log("EchoServer");
-    
-        _init(remdest);
-    }
-    
-    /**
-     * Creates an EchoClient given a destination in base64
-     */
-    public EchoClient(String destStr) throws DataFormatException
-    {
-        _log = new Log("EchoServer");
-    
-        Destination remdest = new Destination();
-        remdest.fromBase64(destStr);
-        _init(remdest);
-    }
-    
-    private void _init(Destination remdest)
-    {
-        dest = remdest;
-    
-        System.out.println("Client: dest="+dest.toBase64());
-    
-        System.out.println("Client: Creating client socketManager");
-    
-        // get a socket manager
-        socketManager = I2PSocketManagerFactory.createManager();
-       
-    }
-
-    /**
-     * runs the EchoClient demo
-     */
-    public void run()
-    {
-        InputStream socketIn;
-        OutputStreamWriter socketOut;
-        OutputStream socketOutStream;
-    
-        System.out.println("Client: Creating connected client socket");
-        System.out.println("dest="+dest.toBase64());
-    
-        try {
-            // get a client socket
-            clientSocket = socketManager.connect(dest);
-        } catch (I2PException e) {
-            e.printStackTrace();
-            return;
-        } catch (ConnectException e) {
-            e.printStackTrace();
-            return;
-        } catch (NoRouteToHostException e) {
-            e.printStackTrace();
-            return;
-        } catch (InterruptedIOException e) {
-            e.printStackTrace();
-            return;
-        }
-    
-        System.out.println("Client: Successfully connected!");
-    
-        try {
-            socketIn = clientSocket.getInputStream();
-            socketOutStream = clientSocket.getOutputStream();
-            socketOut = new OutputStreamWriter(socketOutStream);
-    
-            System.out.println("Client: created streams");
-        
-            socketOut.write("Hi there server!\n");
-            socketOut.flush();
-            
-            System.out.println("Client: sent to server, awaiting reply");
-        
-            String line = DataHelper.readLine(socketIn);
-    
-            System.out.println("Got reply: '" + line + "'");            
-        
-            clientSocket.close();
-    
-        } catch (NoRouteToHostException e) {
-            e.printStackTrace();
-            return;
-        } catch (IOException e) {
-            System.out.println("IOException!!");
-            e.printStackTrace();
-            return;
-        }
-    
-    }
-
-    /**
-     * allows the echo client to be run from a command shell
-     */
-    public static void main(String [] args)
-    {
-        String dest64 = args[0];
-        System.out.println("dest="+dest64);
-    
-        Destination d = new Destination();
-        try {
-            d.fromBase64(dest64);
-        } catch (DataFormatException e) {
-            e.printStackTrace();
-            return;
-        }
-        
-        EchoClient client = new EchoClient(d);
-        
-        System.out.println("client: running");
-        client.run();
-    
-        System.out.println("client: done");
-    
-    }
-    
-}
-
-
-
diff --git a/apps/q/java/src/net/i2p/aum/EchoServer.java b/apps/q/java/src/net/i2p/aum/EchoServer.java
deleted file mode 100644
index 0e77d8e4420440b286285de321d6945d883c378e..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/EchoServer.java
+++ /dev/null
@@ -1,167 +0,0 @@
-
-package net.i2p.aum;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStreamWriter;
-import java.net.ConnectException;
-import java.util.Properties;
-
-import net.i2p.I2PException;
-import net.i2p.client.streaming.I2PServerSocket;
-import net.i2p.client.streaming.I2PSocket;
-import net.i2p.client.streaming.I2PSocketManager;
-import net.i2p.client.streaming.I2PSocketManagerFactory;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Destination;
-import net.i2p.util.Log;
-
-/**
- * a simple program which illustrates the use of I2P stream
- * sockets from a server point of view
- */
-public class EchoServer extends Thread
-{
-    //public I2PClient client;
-    //public PrivDestination privDest;
-    //public I2PSession serverSession;
-    
-    public I2PSocketManager socketManager;
-    public I2PServerSocket serverSocket;
-    
-    public PrivDestination key;
-    public Destination dest;
-    
-    protected static Log _log;
-    
-    public EchoServer() throws I2PException, IOException
-    {
-        _log = new Log("EchoServer");
-    
-        System.out.println("Server: creating new key");
-    
-    //    key = PrivDestination.newKey();
-    //    System.out.println("Server: dest=" + key.toDestinationBase64());
-    
-        System.out.println("Server: creating socket manager");
-    
-        Properties props = new Properties();
-        props.setProperty("inbound.length", "0");
-        props.setProperty("outbound.length", "0");
-        props.setProperty("inbound.lengthVariance", "0");
-        props.setProperty("outbound.lengthVariance", "0");
-    
-        PrivDestination key = PrivDestination.newKey();
-    
-        // get a socket manager
-    //    socketManager = I2PSocketManagerFactory.createManager(key);
-        socketManager = I2PSocketManagerFactory.createManager(key.getInputStream(), props);
-    
-        System.out.println("Server: getting server socket");
-    
-        // get a server socket
-        serverSocket = socketManager.getServerSocket();
-    
-        System.out.println("Server: got server socket, ready to run");
-    
-        dest = socketManager.getSession().getMyDestination();
-        
-        System.out.println("Server: getMyDestination->"+dest.toBase64());
-        
-        start();
-    
-    }
-
-    /**
-     * run this EchoServer
-     */
-    public void run()
-    {
-        System.out.println("Server: listening on dest:");
-    
-    /**
-        try {
-            System.out.println(key.toDestinationBase64());
-        } catch (DataFormatException e) {
-            e.printStackTrace();
-        }
-    */
-    
-        System.out.println(dest.toBase64());
-    
-        while (true)
-        {
-            try {
-                I2PSocket sessSocket = serverSocket.accept();
-                
-                System.out.println("Server: Got connection from client");
-    
-                InputStream socketIn = sessSocket.getInputStream();
-                OutputStreamWriter socketOut = new OutputStreamWriter(sessSocket.getOutputStream());
-    
-                System.out.println("Server: created streams");
-    
-                // read a line from input, and echo it back
-                String line = DataHelper.readLine(socketIn);
-                
-                System.out.println("Server: got '" + line + "'");
-    
-                String reply = "EchoServer: got '" + line + "'\n";
-                socketOut.write(reply);
-                socketOut.flush();
-    
-                System.out.println("Server: sent trply");
-    
-                sessSocket.close();
-    
-                System.out.println("Server: closed socket");
-    
-            } catch (ConnectException e) {
-                e.printStackTrace();
-            } catch (IOException e) {
-                e.printStackTrace();
-            } catch (I2PException e) {
-                e.printStackTrace();
-            }
-    
-        }
-    
-    }
-    
-    public Destination getDest() throws DataFormatException
-    {
-    //    return key.toDestination();
-        return dest;
-    }
-    
-    public String getDestBase64() throws DataFormatException
-    {
-    //    return key.toDestinationBase64();
-        return dest.toBase64();
-    }
-
-    /**
-     * runs EchoServer from the command shell
-     */
-    public static void main(String [] args)
-    {
-        System.out.println("Constructing an EchoServer");
-    
-        try {
-            EchoServer myServer = new EchoServer();
-            System.out.println("Got an EchoServer");
-            System.out.println("Here's the dest:");
-            System.out.println(myServer.getDestBase64());
-    
-            myServer.run();
-    
-        } catch (I2PException e) {
-            e.printStackTrace();
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-    
-    }
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/EchoTest.java b/apps/q/java/src/net/i2p/aum/EchoTest.java
deleted file mode 100644
index 5ba4cf56f212ca506ff48fc363fd493d1676e188..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/EchoTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// runs EchoServer and EchoClient as threads
-
-package net.i2p.aum;
-
-import java.io.IOException;
-
-import net.i2p.I2PException;
-import net.i2p.data.Destination;
-
-/**
- * A simple program which runs the EchoServer and EchoClient
- * demos as threads
- */
-
-public class EchoTest
-{
-    /**
-     * create one instance each of EchoServer and EchoClient,
-     * run the server as a thread, run the client in foreground,
-     * display detailed results
-     */
-    public static void main(String [] args)
-    {
-        EchoServer server;
-        EchoClient client;
-    
-        try {
-            server = new EchoServer();
-            Destination serverDest = server.getDest();
-    
-            System.out.println("EchoTest: serverDest=" + serverDest.toBase64());
-    
-            client = new EchoClient(serverDest);
-    
-        } catch (I2PException e) {
-            e.printStackTrace(); return;
-        } catch (IOException e) {
-            e.printStackTrace(); return;
-        }
-    
-        System.out.println("Starting server...");
-        //server.start();
-    
-        System.out.println("Starting client...");
-        client.run();
-    
-    }
-    
-}
-
-
diff --git a/apps/q/java/src/net/i2p/aum/EmbargoedQueue.java b/apps/q/java/src/net/i2p/aum/EmbargoedQueue.java
deleted file mode 100644
index 384224f7b6323c46c0b2bc2ff91259a6d3722b7e..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/EmbargoedQueue.java
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * SimpleScheduler.java
- *
- * Created on March 24, 2005, 11:14 PM
- */
-
-package net.i2p.aum;
-
-import java.util.Date;
-import java.util.Random;
-import java.util.Vector;
-
-/**
- * <p>Implements a queue of objects, where each object is 'embargoed'
- * against release until a given time. Threads which attempt to .get
- * items from this queue will block if the queue is empty, or if the
- * first item of the queue has a 'release time' which has not yet passed.</p>
- *
- * <p>Think of it like a news desk which receives media releases which are
- * 'embargoed' till a certain time. These releases sit in a queue, and when
- * their embargo expires, they are actioned and go to print or broadcast.
- * The reporters at this news desk are the 'threads', which get blocked
- * until the next item's embargo expires.</p>
- *
- * <p>Purpose of implementing this is to provide a mechanism for scheduling
- * background jobs to be executed at precise times</p>.
- */
-public class EmbargoedQueue extends Thread {
-
-    /**
-     * items which are waiting for dispatch - stored as 2-element vectors,
-     * where elem 0 is Integer dispatch time, and elem 1 is the object;
-     * note that this list is kept in strict ascending order of time.
-     * Whenever an object becomes ready, it is removed from this queue
-     * and appended to readyItems
-     */
-    public Vector waitingItems;
-
-    /**
-     * items which are ready for dispatch (their time has come). 
-     */
-    public SimpleQueue readyItems;
-
-    /** set this true to enable verbose debug messages */
-    public boolean debug = false;
-
-    /** Creates a new embargoed queue */
-    public EmbargoedQueue() {
-        waitingItems = new Vector();
-        readyItems = new SimpleQueue();
-        
-        // fire up scheduler thread
-        start();
-    }
-
-    /**
-     * fetches the item at head of queue, blocking if queue is empty
-     */
-    public Object get()
-    {
-        return readyItems.get();
-    }
-
-    /**
-     * adds a new object to queue without any embargo (or, an embargo that expires
-     * immediately)
-     * @param item the object to be added
-     */
-    public synchronized void putNow(Object item)
-    {
-        putAfter(0, item);
-    }
-
-    /**
-     * adds a new object to queue, embargoed until given number of milliseconds
-     * have elapsed
-     * @param delay number of milliseconds from now when embargo expires
-     * @param item the object to be added
-     */
-    public synchronized void putAfter(long delay, Object item)
-    {
-        long now = new Date().getTime();
-        putAt(now+delay, item);
-    }
-    
-    /**
-     * adds a new object to the queue, embargoed until given time
-     * @param time the unixtime in milliseconds when the object's embargo expires,
-     * and the object is to be made available
-     * @param item the object to be added
-     */
-    public synchronized void putAt(long time, Object item)
-    {
-        Vector elem = new Vector();
-        elem.addElement(new Long(time));
-        elem.addElement(item);
-
-        long now = new Date().getTime();
-        long future = time - now;
-        //System.out.println("putAt: time="+time+" ("+future+"ms from now), job="+item);
-
-        // find where to insert
-        int i;
-        int nitems = waitingItems.size();
-        for (i = 0; i < nitems; i++)
-        {
-            // get item i
-            Vector itemI = (Vector)waitingItems.get(i);
-            long timeI = ((Long)(itemI.get(0))).longValue();
-            if (time < timeI)
-            {
-                // new item earlier than item i, insert here and bust out
-                waitingItems.insertElementAt(elem, i);
-                break;
-            }
-        }
-                
-        // did we insert?
-        if (i == nitems)
-        {
-           // no - gotta append
-           waitingItems.addElement(elem);
-        }
-        
-        // debugging
-        if (debug) {
-            printWaiting();
-        }
-                
-        // awaken this scheduler object's thread, so it can
-        // see if any jobs are ready
-        //notify();
-        interrupt();
-    }
-
-    /**
-     * for debugging - prints out a list of waiting items
-     */
-    public synchronized void printWaiting()
-    {
-        int i;
-        long now = new Date().getTime();
-
-        System.out.println("EmbargoedQueue dump:");
-
-        System.out.println("  Waiting items:");
-        int nwaiting = waitingItems.size();
-        for (i = 0; i < nwaiting; i++)
-        {
-            Vector item = (Vector)waitingItems.get(i);
-            long when = ((Long)item.get(0)).longValue();
-            Object job = item.get(1);
-            int delay = (int)(when - now)/1000;
-            System.out.println("    "+delay+"s, t="+when+", job="+job);
-        }
-
-        System.out.println("  Ready items:");
-        int nready = readyItems.items.size();
-        for (i = 0; i < nready; i++)
-        {
-            //Vector item = (Vector)readyItems.items.get(i);
-            Object item = readyItems.items.get(i);
-            System.out.println("    job="+item);
-        }
-
-    }
-    
-    /**
-     * scheduling thread, which wakes up every time a new job is queued, and
-     * if any jobs are ready, transfers them to the readyQueue and notifies
-     * any waiting client threads
-     */
-    public void run()
-    {
-        // monitor the waiting queue, waiting till one becomes ready
-        while (true)
-        {
-            try {
-                if (waitingItems.size() > 0)
-                {
-                    // at least 1 waiting item
-                    Vector item = (Vector)(waitingItems.get(0));
-                    long now = new Date().getTime();
-                    long then = ((Long)item.get(0)).longValue();
-                    long delay = then - now;
-                    
-                    // ready?
-                    if (delay <= 0)
-                    {
-                        // yep, ready, remove job and stick on waiting queue
-                        waitingItems.remove(0);         // ditch from waiting
-                        Object elem = item.get(1);
-                        readyItems.put(elem);    // and add to ready
-
-                        if (debug)
-                        {
-                            System.out.println("embargo expired on "+elem);
-                            printWaiting();
-                        }
-                    }
-                    else
-                    {
-                        // not ready, hang about till we get woken, or the
-                        // job becomes ready
-                        if (debug)
-                        {
-                            System.out.println("waiting for "+delay+"ms");
-                        }
-                        Thread.sleep(delay);
-                    }
-                }
-                else
-                {
-                    // no items yet, hang out for an interrupt
-                    if (debug)
-                    {
-                        System.out.println("queue is empty");
-                    }
-                    synchronized (this) {
-                        wait();
-                    }
-                }
-            } catch (Exception e) {
-                //System.out.println("exception");
-                if (debug)
-                {
-                    System.out.println("exception ("+e.getClass().getName()+") "+e.getMessage());
-                }
-            }
-        }
-    }
-    
-    private static class TestThread extends Thread {
-        
-        String id;
-        
-        EmbargoedQueue q;
-        
-        public TestThread(String id, EmbargoedQueue q) {
-            this.id = id;
-            this.q = q;
-        }
-        
-        public void run() {
-            try {
-                print("waiting for queue");
-
-                Object item = q.get();
-                
-                print("got item: '"+item+"'");
-                
-            } catch (Exception e) {
-                e.printStackTrace();
-                return;
-            }
-        }
-        
-        public void print(String msg) {
-            System.out.println("thread '"+id+"': "+msg);
-        }
-        
-    }
-
-    /**
-     * @param args the command line arguments
-     */
-    public static void main(String[] args) {
-
-        int i;
-        int nthreads = 7;
-
-        Thread [] threads = new Thread[nthreads];
-
-        EmbargoedQueue q = new EmbargoedQueue();
-        SimpleSemaphore threadPool = new SimpleSemaphore(nthreads);
-
-        // populate the queue with some stuff
-        q.putAfter(10000, "red");
-        q.putAfter(3000, "orange");
-        q.putAfter(6000, "yellow");
-        
-        // populate threads array
-        for (i = 0; i < nthreads; i++) {
-            threads[i] = new TestThread("thread"+i, q);
-        }
-        
-        // and launch the threads
-        for (i = 0; i < nthreads; i++) {
-            threads[i].start();
-        }
-
-        // wait, presumably till all these elements are actioned
-        try {
-            Thread.sleep(12000);
-        } catch (Exception e) {
-            e.printStackTrace();
-            return;
-        }
-
-        // add some more shit to the queue, randomly scheduled
-        Random r = new Random();
-        String [] items = {"green", "blue", "indigo", "violet", "black", "white", "brown"};
-        for (i = 0; i < items.length; i++) {
-            String item = items[i];
-            int delay = 2000 + r.nextInt(8000);
-            System.out.println("main: adding '"+item+"' after "+delay+"ms ...");
-            q.putAfter(delay, item);
-        }
-
-        // wait, presumably for all jobs to finish
-        try {
-            Thread.sleep(12000);
-        } catch (Exception e) {
-            e.printStackTrace();
-            return;
-        }
-
-        System.out.println("main: terminating");
-
-    }
-    
-}
diff --git a/apps/q/java/src/net/i2p/aum/I2PCat.java b/apps/q/java/src/net/i2p/aum/I2PCat.java
deleted file mode 100644
index 35be1ad12032e1bbe4da924fb6151b8bfef45524..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/I2PCat.java
+++ /dev/null
@@ -1,460 +0,0 @@
-
-// I2P equivalent of 'netcat'
-
-package net.i2p.aum;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.net.ConnectException;
-import java.net.NoRouteToHostException;
-import java.util.Properties;
-
-import net.i2p.I2PAppContext;
-import net.i2p.I2PException;
-import net.i2p.client.naming.HostsTxtNamingService;
-import net.i2p.client.streaming.I2PServerSocket;
-import net.i2p.client.streaming.I2PSocket;
-import net.i2p.client.streaming.I2PSocketManager;
-import net.i2p.client.streaming.I2PSocketManagerFactory;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Destination;
-import net.i2p.util.Log;
-
-/**
- * A I2P equivalent of the much-beloved 'netcat' utility.
- * This command-line utility can either connect to a remote
- * destination, or listen on a private destination for incoming
- * connections. Once a connection is established, input on stdin
- * is sent to the remote peer, and anything received from the
- * remote peer is printed to stdout
- */
-
-public class I2PCat extends Thread
-{
-    public I2PSocketManager socketManager;
-    public I2PServerSocket serverSocket;
-    public I2PSocket sessSocket;
-    
-    public PrivDestination key;
-    public Destination dest;
-    
-    public InputStream socketIn;
-    public OutputStream socketOutStream;
-    public OutputStreamWriter socketOut;
-    
-    public SockInput rxThread;
-    
-    protected static Log _log;
-
-    public static String defaultHost = "127.0.0.1";
-    public static int defaultPort = 7654;
-    
-    /**
-     * a thread for reading from socket and displaying on stdout
-     */
-    private class SockInput extends Thread {
-    
-        InputStream _in;
-        
-        protected Log _log;
-        public SockInput(InputStream i) {
-        
-            _in = i;
-        }
-        
-        public void run()
-        {
-            // the thread portion, receives incoming bytes on
-            // the socket input stream and spits them to stdout
-        
-            byte [] ch = new byte[1];
-        
-            print("Receiver thread listening...");
-        
-            try {
-                while (true) {
-        
-                    //String line = DataHelper.readLine(socketIn);
-                    if (_in.read(ch) != 1) {
-                        print("failed to receive from socket");
-                        break;
-                    }
-        
-                    //System.out.println(line);
-                    System.out.write(ch, 0, 1);
-                    System.out.flush();
-                }
-            } catch (IOException e) {
-                e.printStackTrace();
-                print("Receiver thread crashed, terminating!!");
-                System.exit(1);
-            }
-        
-        }
-        
-        
-        void print(String msg)
-        {
-            System.out.println("-=- I2PCat: "+msg);
-        
-            if (_log != null) {
-                _log.debug(msg);
-            }
-        
-        }
-        
-        
-    }
-    
-    
-    public I2PCat()
-    {
-        _log = new Log("I2PCat");
-    
-    }
-    
-    /**
-     * Runs I2PCat in server mode, listening on the given destination
-     * for one incoming connection. Once connection is established, 
-     * copyies data between the remote peer and
-     * the local terminal console.
-     */
-    public void runServer(String keyStr) throws IOException, DataFormatException
-    {
-        Properties props = new Properties();
-        props.setProperty("inbound.length", "0");
-        props.setProperty("outbound.length", "0");
-        props.setProperty("inbound.lengthVariance", "0");
-        props.setProperty("outbound.lengthVariance", "0");
-    
-        // generate new key if needed
-        if (keyStr.equals("new")) {
-    
-            try {
-                key = PrivDestination.newKey();
-            } catch (I2PException e) {
-                e.printStackTrace();
-                return;
-            } catch (IOException e) {
-                e.printStackTrace();
-                return;
-            }
-    
-            print("Creating new server dest...");
-    
-            socketManager = I2PSocketManagerFactory.createManager(key.getInputStream(), props);
-    
-            print("Getting server socket...");
-    
-            serverSocket = socketManager.getServerSocket();
-    
-            print("Server socket created, ready to run...");
-    
-            dest = socketManager.getSession().getMyDestination();
-            
-            print("private key follows:");
-            System.out.println(key.toBase64());
-        
-            print("dest follows:");
-            System.out.println(dest.toBase64());
-    
-        }
-    
-        else {
-    
-            key = PrivDestination.fromBase64String(keyStr);
-    
-            String dest64Abbrev = key.toBase64().substring(0, 16);
-    
-            print("Creating server socket manager on dest "+dest64Abbrev+"...");
-    
-            socketManager = I2PSocketManagerFactory.createManager(key.getInputStream(), props);
-    
-            serverSocket = socketManager.getServerSocket();
-    
-            print("Server socket created, ready to run...");
-        }
-
-        print("Awaiting client connection...");
-    
-        I2PSocket sessSocket;
-    
-        try {
-            sessSocket = serverSocket.accept();
-        } catch (I2PException e) {
-            e.printStackTrace();
-            return;
-        } catch (ConnectException e) {
-            e.printStackTrace();
-            return;
-        }
-                
-        print("Got connection from client");
-    
-        chat(sessSocket);
-    
-    }
-
-    public void runClient(String destStr)
-        throws DataFormatException, IOException
-    {
-        runClient(destStr, defaultHost, defaultPort);
-    }
-
-    /**
-     * runs I2PCat in client mode, connecting to a remote
-     * destination then copying data between the remote peer and
-     * the local terminal console
-     */
-    public void runClient(String destStr, String host, int port)
-        throws DataFormatException, IOException
-    {
-        // accept 'file:' prefix
-        if (destStr.startsWith("file:", 0))
-        {
-            String path = destStr.substring(5);
-            destStr = new SimpleFile(path, "r").read();
-        }
-    
-        else if (destStr.length() < 255) {
-            // attempt hosts file lookup
-            I2PAppContext ctx = new I2PAppContext();
-            HostsTxtNamingService h = new HostsTxtNamingService(ctx);
-            Destination dest1 = h.lookup(destStr);
-            if (dest1 == null) {
-                usage("Cannot resolve hostname: '"+destStr+"'");
-            }
-            
-            // successful lookup
-            runClient(dest1, host, port);
-        }
-    
-        else {
-            // otherwise, bigger strings are assumed to be base64 dests
-    
-            Destination dest = new Destination();
-            dest.fromBase64(destStr);
-            runClient(dest, host, port);
-        }
-    }
-
-    public void runClient(Destination dest) {
-        runClient(dest, "127.0.0.1", 7654);
-    }
-
-    /**
-     * An alternative constructor which accepts an I2P Destination object
-     */
-    public void runClient(Destination dest, String host, int port)
-    {
-        this.dest = dest;
-        
-        String destAbbrev = dest.toBase64().substring(0, 16)+"...";
-    
-        print("Connecting via i2cp "+host+":"+port+" to destination "+destAbbrev+"...");
-        System.out.flush();
-    
-        try {
-            // get a socket manager
-            socketManager = I2PSocketManagerFactory.createManager(host, port);
-
-            // get a client socket
-            print("socketManager="+socketManager);
-
-            sessSocket = socketManager.connect(dest);
-    
-        } catch (I2PException e) {
-            e.printStackTrace();
-            return;
-        } catch (ConnectException e) {
-            e.printStackTrace();
-            return;
-        } catch (NoRouteToHostException e) {
-            e.printStackTrace();
-            return;
-        } catch (InterruptedIOException e) {
-            e.printStackTrace();
-            return;
-        }
-    
-        print("Successfully connected!");
-        print("(Press Control-C to quit)");
-    
-        // Perform console interaction
-        chat(sessSocket);
-    
-        try {
-            sessSocket.close();
-    
-        } catch (IOException e) {
-            e.printStackTrace();
-            return;
-        }
-    }
-
-    /**
-     * Launch the background thread to copy incoming data to stdout, then
-     * loop in foreground copying lines from stdin and sending them to remote peer
-     */
-    public void chat(I2PSocket sessSocket) {
-    
-        try {
-            socketIn = sessSocket.getInputStream();
-            socketOutStream = sessSocket.getOutputStream();
-            socketOut = new OutputStreamWriter(socketOutStream);
-    
-            // launch receiver thread
-            start();
-            //launchRx();
-        
-            while (true) {
-        
-                String line = DataHelper.readLine(System.in);
-                print("sent: '"+line+"'");
-    
-                socketOut.write(line+"\n");
-                socketOut.flush();
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-            return;
-        }
-    
-    }
-    
-    /**
-     * executes in a thread, receiving incoming bytes on
-     * the socket input stream and spitting them to stdout
-     */
-    public void run()
-    {
-    
-        byte [] ch = new byte[1];
-    
-        print("Receiver thread listening...");
-    
-        try {
-            while (true) {
-    
-                //String line = DataHelper.readLine(socketIn);
-                if (socketIn.read(ch) != 1) {
-                    print("failed to receive from socket");
-                    break;
-                }
-    
-                //System.out.println(line);
-                System.out.write(ch, 0, 1);
-                System.out.flush();
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-            print("Receiver thread crashed, terminating!!");
-            System.exit(1);
-        }
-    
-    }
-    
-    
-    public void launchRx() {
-    
-        rxThread = new SockInput(socketIn);
-        rxThread.start();
-    
-    }
-    
-    static void print(String msg)
-    {
-        System.out.println("-=- I2PCat: "+msg);
-    
-        if (_log != null) {
-            _log.debug(msg);
-        }
-    
-    }
-    
-    public static void usage(String msg)
-    {
-        usage(msg, 1);
-    }
-    
-    public static void usage(String msg, int ret)
-    {
-        System.out.println(msg);
-        usage(ret);
-    }
-    
-    public static void usage(int ret)
-    {
-        System.out.print(
-            "This utility is an I2P equivalent of the standard *nix 'netcat' utility\n"+
-            "usage:\n"+
-            "  net.i2p.aum.I2PCat [-h]\n"+
-            "    - display this help\n"+
-            "  net.i2p.aum.I2PCat dest [host [port]]\n"+
-            "    - run in client mode, 'dest' should be one of:\n"+
-            "      hostname.i2p - an I2P hostname listed in hosts.txt\n"+
-            "         (only works with a hosts.txt in current directory)\n"+
-            "      base64dest - a full base64 destination string\n"+
-            "      file:b64filename - filename of a file containing base64 dest\n"+
-            "  net.i2p.aum.I2PCat -l privkey\n"+
-            "    - run in server mode, 'key' should be one of:\n"+
-            "      base64privkey - a full base64 private key string\n"+
-            "      file:b64filename - filename of a file containing base64 privkey\n"+
-            "\n"
-            );
-        System.exit(ret);
-    }
-    
-    public static void main(String [] args) throws IOException, DataFormatException
-    {
-        int argc = args.length;
-    
-        // barf if no args
-        if (argc == 0) {
-            usage("Missing argument");
-        }
-    
-        // show help on request
-        if (args[0].equals("-h") || args[0].equals("--help")) {
-            usage(0);
-        }
-    
-        // server or client?
-        if (args[0].equals("-l")) {
-            if (argc != 2) {
-                usage("Bad argument count");
-            }
-    
-            new I2PCat().runServer(args[1]);
-        }
-        else {
-            // client mode - barf if not 1-3 args
-            if (argc < 1 || argc > 3) {
-                usage("Bad argument count");
-            }
-    
-            try {
-                int port = defaultPort;
-                String host = defaultHost;
-                if (args.length > 1) {
-                    host = args[1];
-                    if (args.length > 2) {
-                        port = new Integer(args[2]).intValue();
-                    }
-                }
-                new I2PCat().runClient(args[0], host, port);
-
-            } catch (DataFormatException e) {
-                e.printStackTrace();
-            }
-        }
-    }
-    
-}
-
-
-
diff --git a/apps/q/java/src/net/i2p/aum/I2PSocketHelper.java b/apps/q/java/src/net/i2p/aum/I2PSocketHelper.java
deleted file mode 100644
index bc3570c5bde8dbc4f9accdd21c69ca386c461398..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/I2PSocketHelper.java
+++ /dev/null
@@ -1,15 +0,0 @@
-
-package net.i2p.aum;
-
-
-/**
- * Class which wraps an I2PSocket object with convenient methods.
- * Nothing presently implemented here.
- */
-
-public class I2PSocketHelper
-{
-
-}
-
-
diff --git a/apps/q/java/src/net/i2p/aum/I2PTunnelXMLObject.java b/apps/q/java/src/net/i2p/aum/I2PTunnelXMLObject.java
deleted file mode 100644
index 7fa82373448811d1415f4eea6eaa1258601ceaa6..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/I2PTunnelXMLObject.java
+++ /dev/null
@@ -1,138 +0,0 @@
-package net.i2p.aum;
-
-import java.util.Hashtable;
-
-import net.i2p.i2ptunnel.I2PTunnelXMLWrapper;
-
-/**
- * Defines the I2P tunnel management methods which will be
- * exposed to XML-RPC clients
- * Methods in this class are forwarded to an I2PTunnelXMLWrapper object
- */
-public class I2PTunnelXMLObject
-{
-    protected I2PTunnelXMLWrapper tunmgr;
-
-    /**
-     * Builds the interface object. You normally shouldn't have to
-     * instantiate this directly - leave it to I2PTunnelXMLServer
-     */
-    public I2PTunnelXMLObject()
-    {
-        tunmgr = new I2PTunnelXMLWrapper();
-    }
-
-    /**
-     * Generates an I2P keypair, returning a dict with keys 'result' (usually 'ok'),
-     * priv' (private key as base64) and 'dest' (destination as base64)
-     */
-    public Hashtable genkeys()
-    {
-        return tunmgr.xmlrpcGenkeys();
-    }
-
-    /**
-     * Get a list of active TCP tunnels currently being managed by this
-     * tunnel manager.
-     * @return a dict with keys 'status' (usually 'ok'),
-     * 'jobs' (a list of dicts representing each job, each with keys 'job' (int, job
-     * number), 'type' (string, 'server' or 'client'), port' (int, the port number).
-     * Also for server, keys 'host' (hostname, string) and 'ip' (IP address, string).
-     * For clients, key 'dest' (string, remote destination as base64).
-     */
-    public Hashtable list()
-    {
-        return tunmgr.xmlrpcList();
-    }
-
-    /**
-     * Attempts to find I2P hostname in hosts.txt.
-     * @param hostname string, I2P hostname
-     * @return dict with keys 'status' ('ok' or 'fail'),
-     * and if successful lookup, 'dest' (base64 destination).
-     */
-    public Hashtable lookup(String hostname)
-    {
-        return tunmgr.xmlrpcLookup(hostname);
-    }
-
-    /**
-     * Attempt to open client tunnel
-     * @param port local port to listen on, int
-     * @param dest remote dest to tunnel to, base64 string
-     * @return dict with keys 'status' (string - 'ok' or 'fail').
-     * If 'ok', also key 'result' with text output from tunnelmgr
-     */
-    public Hashtable client(int port, String dest)
-    {
-        return tunmgr.xmlrpcClient(port, dest);
-    }
-
-    /**
-     * Attempts to open server tunnel
-     * @param host TCP hostname of TCP server to tunnel to
-     * @param port number of TCP server
-     * @param key - base64 private key to receive I2P connections on
-     * @return dict with keys 'status' (string, 'ok' or 'fail').
-     * if 'fail', also a key 'error' with explanatory text.
-     */
-    public Hashtable server(String host, int port, String key)
-    {
-        return tunmgr.xmlrpcServer(host, port, key);
-    }
-
-    /**
-     * Close an existing tunnel
-     * @param jobnum (int) job number of connection to close
-     * @return dict with keys 'status' (string, 'ok' or 'fail')
-     */
-    public Hashtable close(int jobnum)
-    {
-        return tunmgr.xmlrpcClose(jobnum);
-    }
-
-    /**
-     * Close an existing tunnel
-     * @param jobnum (string) job number of connection to close as string,
-     * 'all' to close all jobs.
-     * @return dict with keys 'status' (string, 'ok' or 'fail')
-     */
-    public Hashtable close(String job)
-    {
-        return tunmgr.xmlrpcClose(job);
-    }
-
-    /**
-     * Close zero or more tunnels matching given criteria
-     * @param criteria A dict containing zero or more of the keys:
-     * 'job' (job number), 'type' (string, 'server' or 'client'),
-     * 'host' (hostname), 'port' (port number),
-     * 'ip' (IP address), 'dest' (string, remote dest)
-     */
-    public Hashtable close(Hashtable criteria)
-    {
-        return tunmgr.xmlrpcClose(criteria);
-    }
-
-    /**
-     * simple method to help with debugging your client prog
-     * @param x an int
-     * @return x + 1
-     */
-    public int bar(int x)
-    {
-        System.out.println("foo invoked");
-        return x + 1;
-    }
-
-    /**
-     * as for bar(int), but returns zero if no arg given
-     */
-    public int bar()
-    {
-        return bar(0);
-    }
-    
-}
-
-
diff --git a/apps/q/java/src/net/i2p/aum/I2PTunnelXMLServer.java b/apps/q/java/src/net/i2p/aum/I2PTunnelXMLServer.java
deleted file mode 100644
index 7c9ebbd0d8877bca45b5381db8f4f5d3226b3369..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/I2PTunnelXMLServer.java
+++ /dev/null
@@ -1,63 +0,0 @@
-
-package net.i2p.aum;
-
-import org.apache.xmlrpc.WebServer;
-
-/**
- * Provides a means for programs in any language to dynamically manage
- * their own I2P <-> TCP tunnels, via simple TCP XML-RPC function calls.
- * This server is presently hardwired to listen on port 22322.
- */
-
-public class I2PTunnelXMLServer
-{
-    protected WebServer ws;
-    protected I2PTunnelXMLObject tunobj;
-    
-    public int port = 22322;
-    
-    // constructor
-    
-    public void _init()
-    {
-        ws = new WebServer(port);
-        tunobj = new I2PTunnelXMLObject();
-        ws.addHandler("i2p.tunnel", tunobj);
-    
-    }
-    
-    
-    // default constructor
-    public I2PTunnelXMLServer()
-    {
-        super();
-        _init();
-    }
-    
-    // constructor which takes shell args
-    public I2PTunnelXMLServer(String args[])
-    {
-        super();
-        _init();
-    }
-    
-    // run the server
-    public void run()
-    {
-        ws.start();
-        System.out.println("I2PTunnel XML-RPC server listening on port "+port);
-        ws.run();
-    
-    }
-    
-    public static void main(String args[])
-    {
-        I2PTunnelXMLServer tun;
-    
-        tun = new I2PTunnelXMLServer();
-        tun.run();
-    }
-    
-}
-
-
diff --git a/apps/q/java/src/net/i2p/aum/I2PXmlRpcClient.java b/apps/q/java/src/net/i2p/aum/I2PXmlRpcClient.java
deleted file mode 100644
index fdf650897aa43f9c4b4398d5b3b660e79b74f421..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/I2PXmlRpcClient.java
+++ /dev/null
@@ -1,65 +0,0 @@
-
-package net.i2p.aum;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-
-import net.i2p.data.Destination;
-import net.i2p.util.Log;
-
-import org.apache.xmlrpc.XmlRpcClient;
-
-
-/**
- * an object which is used to invoke methods on remote I2P XML-RPC
- * servers. You should not instantiate these objects directly, but
- * create them through
- * {@link net.i2p.aum.I2PXmlRpcClientFactory#newClient(Destination) I2PXmlRpcClientFactory.newClient()}
- * Note that this is really just a thin wrapper around XmlRpcClient, mostly for reasons
- * of consistency with I2PXmlRpcServer[Factory].
- */
-
-public class I2PXmlRpcClient extends XmlRpcClient
-{
-    public static boolean debug = false;
-    
-    protected static Log _log;
-    
-    /**
-     * Construct an I2P XML-RPC client with this URL.
-     * Note that you should not
-     * use this constructor directly - use I2PXmlRpcClientFactory.newClient() instead
-     */
-    public I2PXmlRpcClient(URL url)
-    {
-        super(url);
-        _log = new Log("I2PXmlRpcClient");
-    
-    }
-    
-    /**
-     * Construct a XML-RPC client for the URL represented by this String.
-     * Note that you should not
-     * use this constructor directly - use I2PXmlRpcClientFactory.newClient() instead
-     */
-    public I2PXmlRpcClient(String url) throws MalformedURLException
-    {
-        super(url);
-        _log = new Log("I2PXmlRpcClientFactory");
-    
-    }
-    
-    /**
-     * Construct a XML-RPC client for the specified hostname and port.
-     * Note that you should not
-     * use this constructor directly - use I2PXmlRpcClientFactory.newClient() instead
-     */
-    public I2PXmlRpcClient(String hostname, int port) throws MalformedURLException
-    {
-        super(hostname, port);
-        _log = new Log("I2PXmlRpcClient");
-    
-    }
-    
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/I2PXmlRpcClientFactory.java b/apps/q/java/src/net/i2p/aum/I2PXmlRpcClientFactory.java
deleted file mode 100644
index 16edc157eadd8e589d7f2105c5b26c152e92db37..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/I2PXmlRpcClientFactory.java
+++ /dev/null
@@ -1,226 +0,0 @@
-
-package net.i2p.aum;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Properties;
-import java.util.Vector;
-
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Destination;
-import net.i2p.util.Log;
-
-import org.apache.xmlrpc.XmlRpcClient;
-
-
-/**
- * Creates I2P XML-RPC client objects, which you can use
- * to issue XML-RPC function calls over I2P.
- * Instantiating this class causes the vm-wide http proxy system
- * properties to be set to the address of the I2P eepProxy host/port.
- * I2PXmlRpcClient objects need to communicate with the I2P
- * eepProxy. If your eepProxy is at the standard localhost:4444 address,
- * you can use the default constructor. Otherwise, you can set this
- * eepProxy address by either (1) passing eepProxy hostname/port to the
- * constructor, or (2) running the jvm with 'eepproxy.tcp.host' and
- * 'eepproxy.tcp.port' system properties set. Note that (1) takes precedence.
- * Failure to set up EepProxy host/port correctly will result in an IOException
- * when you invoke .execute() on your client objects.
- * Invoke this class from your shell to see a demo
- */
-
-public class I2PXmlRpcClientFactory
-{
-    public static boolean debug = false;
-    
-    public static String _defaultEepHost = "127.0.0.1";
-    public static int    _defaultEepPort = 4444;
-
-    protected static Log _log;
-    
-    /**
-     * Create an I2P XML-RPC client factory, and set it to create
-     * clients of a given class.
-     * @param clientClass a class to use when creating new clients
-     */
-    public I2PXmlRpcClientFactory()
-    {
-        this(null, 0);
-    }
-
-    /**
-     * Create an I2P XML-RPC client factory, and set it to create
-     * clients of a given class, and dispatch calls through a non-standard
-     * eepProxy.
-     * @param eepHost the eepProxy TCP hostname
-     * @param eepPort the eepProxy TCP port number
-     */
-    public I2PXmlRpcClientFactory(String eepHost, int eepPort)
-    {
-        String eepPortStr;
-
-        _log = new Log("I2PXmlRpcClientFactory");
-        _log.shouldLog(Log.DEBUG);
-    
-        Properties p = System.getProperties();
-
-        // determine what actual eepproxy host/port we're using
-        if (eepHost == null) {
-            eepHost = p.getProperty("eepproxy.tcp.host", _defaultEepHost);
-        }
-        if (eepPort > 0) {
-            eepPortStr = String.valueOf(eepPort);
-        }
-        else {
-            eepPortStr = p.getProperty("eepproxy.tcp.port");
-            if (eepPortStr == null) {
-                eepPortStr = String.valueOf(_defaultEepPort);
-            }
-        }
-        
-        p.put("proxySet", "true");
-        p.put("http.proxyHost", eepHost);
-        p.put("http.proxyPort", eepPortStr);
-    }
-
-    /**
-     * Create an I2P XML-RPC client object, which is subsequently used for
-     * dispatching XML-RPC requests.
-     * @param dest - an I2P destination object, comprising the
-     * destination of the remote
-     * I2P XML-RPC server.
-     * @return a new XmlRpcClient object (refer org.apache.xmlrpc.XmlRpcClient).
-     */
-    public I2PXmlRpcClient newClient(Destination dest) throws MalformedURLException {
-
-        return newClient(new URL("http", "i2p/"+dest.toBase64(), "/"));
-    }
-    
-    /**
-     * Create an I2P XML-RPC client object, which is subsequently used for
-     * dispatching XML-RPC requests.
-     * @param hostOrDest - an I2P hostname (listed in hosts.txt) or a
-     * destination base64 string, for the remote I2P XML-RPC server
-     * @return a new XmlRpcClient object (refer org.apache.xmlrpc.XmlRpcClient).
-     */
-    public I2PXmlRpcClient newClient(String hostOrDest)
-        throws DataFormatException, MalformedURLException
-    {
-        String hostname;
-        URL u;
-    
-        try {
-            // try to make a dest out of the string
-            Destination dest = new Destination();
-            dest.fromBase64(hostOrDest);
-    
-            // converted ok, treat as valid dest, form i2p/blahblah url from it
-            I2PXmlRpcClient client = newClient(new URL("http", "i2p/"+hostOrDest, "/"));
-            client.debug = debug;
-            return client;
-    
-        } catch (DataFormatException e) {
-    
-            if (debug) {
-                e.printStackTrace();
-                print("hostOrDest length="+hostOrDest.length());
-            }
-    
-            // failed to load up a dest, test length
-            if (hostOrDest.length() < 255) {
-                // short-ish, assume a hostname
-                u = new URL("http", hostOrDest, "/");
-                I2PXmlRpcClient client = newClient(u);
-                client.debug = debug;
-                return client;
-            }
-            else {
-                // too long for a host, barf
-                throw new DataFormatException("Bad I2P hostname/dest:\n"+hostOrDest);
-            }
-        }
-    }
-
-    /**
-     * Create an I2P XML-RPC client object, which is subsequently used for
-     * dispatching XML-RPC requests. This method is not recommended.
-     * @param u - a URL object, containing the URL of the remote
-     * I2P XML-RPC server, for example, "http://xmlrpc.aum.i2p" (assuming
-     * there's a hosts.txt entry for 'xmlrpc.aum.i2p'), or
-     * "http://i2p/base64destblahblah...". Note that if you use this method
-     * directly, the created XML-RPC client object will ONLY work if you
-     * instantiate the URL object as 'new URL("http", "i2p/"+host-or-dest, "/")'.
-     */
-    protected I2PXmlRpcClient newClient(URL u)
-    {
-        Object [] args = { u };
-        //return new I2PXmlRpcClient(u);
-        
-        // construct and return a client object of required class
-        return new I2PXmlRpcClient(u);
-    }
-    
-    /**
-     * Runs a demo of an I2P XML-RPC client. Assumes you have already
-     * launched an I2PXmlRpcServerFactory demo, because it gets its
-     * dest from the file 'demo.dest64' created by I2PXmlRpcServerFactory demo.
-     *
-     * Ensure you have first launched net.i2p.aum.I2PXmlRpcServerFactory
-     * from your command line.
-     */
-    public static void main(String [] args) {
-    
-        String destStr;
-    
-        debug = true;
-    
-        try {
-            print("Creating client factory...");
-            
-            I2PXmlRpcClientFactory f = new I2PXmlRpcClientFactory();
-        
-            print("Creating new client...");
-    
-            if (args.length == 0) {
-                print("Reading dest from demo.dest64");
-                destStr = new SimpleFile("demo.dest64", "r").read();
-            }
-            else {
-                destStr = args[0];
-            }
-                
-            XmlRpcClient c = f.newClient(destStr);
-        
-            print("Invoking foo...");
-            
-            Vector v = new Vector();
-            v.add("one");
-            v.add("two");
-        
-            Object res = c.execute("foo.bar", v);
-            
-            print("Got back object: " + res);
-    
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        
-    }
-    /**
-     * Used for internal debugging
-     */
-    protected static void print(String msg)
-    {
-        if (debug) {
-            System.out.println("I2PXmlRpcClient: " + msg);
-    
-            if (_log != null) {
-                System.out.println("LOGGING SOME SHIT");
-                _log.debug(msg);
-            }
-        }
-    }
-}
-
-
-
diff --git a/apps/q/java/src/net/i2p/aum/I2PXmlRpcDemoClass.java b/apps/q/java/src/net/i2p/aum/I2PXmlRpcDemoClass.java
deleted file mode 100644
index a8de8e7913f221a83647f12be2f14cb03630d7fe..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/I2PXmlRpcDemoClass.java
+++ /dev/null
@@ -1,21 +0,0 @@
-
-package net.i2p.aum;
-
-
-/**
- * A simple class providing callable xmlrpc server methods, gets linked in to
- * the server demo.
- */
-public class I2PXmlRpcDemoClass
-{
-    public int add1(int n) {
-        return n + 1;
-    }
-    
-    public String bar(String arg1, String arg2) {
-        System.out.println("Demo: got hit to bar: arg1='"+arg1+"', arg2='"+arg2+"'");
-        return "I2P demo xmlrpc server(foo.bar): arg1='"+arg1+"', arg2='"+arg2+"'";
-    }
-
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/I2PXmlRpcServer.java b/apps/q/java/src/net/i2p/aum/I2PXmlRpcServer.java
deleted file mode 100644
index 82854b00f7db82feac2da4718054ddd6f80f2894..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/I2PXmlRpcServer.java
+++ /dev/null
@@ -1,433 +0,0 @@
-package net.i2p.aum;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStreamWriter;
-import java.util.Date;
-import java.util.Properties;
-
-import net.i2p.I2PAppContext;
-import net.i2p.I2PException;
-import net.i2p.client.streaming.I2PServerSocket;
-import net.i2p.client.streaming.I2PSocket;
-import net.i2p.client.streaming.I2PSocketManager;
-import net.i2p.client.streaming.I2PSocketManagerFactory;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Destination;
-
-import org.apache.xmlrpc.XmlRpcServer;
-
-
-/**
- * An XML-RPC server which works completely within I2P, listening
- * on a dest for requests.
- * You should not instantiate this class directly, but instead create
- * an I2PXmlRpcServerFactory object, and use its .newServer() method
- * to create a server object.
- */
-public class I2PXmlRpcServer extends XmlRpcServer implements Runnable
-{
-    public class I2PXmlRpcServerWorkerThread extends Thread {
-
-        I2PSocket _sock;
-        
-        public I2PXmlRpcServerWorkerThread(I2PSocket sock) {
-            _sock = sock;
-        }
-        
-        public void run() {
-            
-            try {
-                System.out.println("I2PXmlRpcServer.run: got inbound XML-RPC I2P conn");
-                
-                log.info("run: Got client connection, creating streams");
-                
-                InputStream socketIn = _sock.getInputStream();
-                OutputStreamWriter socketOut = new OutputStreamWriter(_sock.getOutputStream());
-                
-                log.info("run: reading http headers");
-                
-                // read headers, determine size of req
-                int size = readHttpHeaders(socketIn);
-                
-                if (size <= 0) {
-                    // bad news
-                    log.info("read req failed, terminating session");
-                    _sock.close();
-                    return;
-                }
-                
-                log.info("run: reading request body of "+size+" bytes");
-                
-                // get raw request body
-                byte [] reqBody = new byte[size];
-                for (int i=0; i<size; i++) {
-                    int b = socketIn.read();
-                    reqBody[i] = (byte)b;
-                }
-                //socketIn.read(reqBody);
-                
-                //log.info("reqBody='" + (new String(reqBody)) + "'");
-                //System.out.println("reqBody='" + (new String(reqBody)) + "'");
-                //System.out.println("reqBody:");
-                //for (int ii=0; ii<reqBody.length; ii++) {
-                //    System.out.println("i=" + ii + " ch="+reqBody[ii]);
-                //}
-                
-                ByteArrayInputStream reqBodyStream = new ByteArrayInputStream(reqBody);
-                
-                log.info("run: executing request");
-                
-                System.out.println("run: executing request");
-                
-                // read and execute full request
-                byte [] result;
-                try {
-                    result = execute(reqBodyStream);
-                } catch (Exception e) {
-                    System.out.println("run: execute failed, closing socket");
-                    _sock.close();
-                    System.out.println("run: closed socket");
-                    throw e;
-                }
-                
-                log.info("run: sending response");
-                
-                
-                // fudge - manual header and response generation
-                socketOut.write(
-                "HTTP/1.0 200 OK\r\n" +
-                "Server: I2P XML-RPC server by aum\r\n" +
-                "Date: " + (new Date().toString()) + "\r\n" +
-                "Content-type: text/xml\r\n" +
-                "Content-length: " + String.valueOf(result.length) + "\r\n" +
-                "\r\n");
-                socketOut.write(new String(result));
-                //socketOut.write(result);
-                socketOut.flush();
-                
-                log.info("closing socket");
-                System.out.println("closing socket");
-                
-                //response.setContentType ("text/xml");
-                //response.setContentLength (result.length());
-                //OutputStream out = response.getOutputStream();
-                //out.write (result);
-                //out.flush ();
-                
-                _sock.close();
-                
-                log.info("session complete");
-            } catch (Exception e) {
-                try {
-                    e.printStackTrace();
-                    _sock.close();
-                } catch (Exception e1) {
-                    e1.printStackTrace();
-                }
-            }
-        }
-    }
-
-    // convenience - dest this server is listening on
-    public Destination dest;
-    
-    // server's socket manager object
-    public I2PSocketManager socketMgr;
-    
-    // server's socket
-    public I2PServerSocket serverSocket;
-
-    /** socket of latest incoming connection */
-    public I2PSocket sessSocket;
-
-    // set to enable debugging msgs
-    public static boolean debug = false;
-    
-    // stream-proented xmlrpc server
-    
-    protected net.i2p.util.Log log;
-    protected I2PAppContext i2p;
-
-    public Thread serverThread;
-
-    /**
-     * (do not use this constructor directly)
-     */
-    
-    public I2PXmlRpcServer(String keyStr, Properties props, I2PAppContext i2p)
-        throws DataFormatException, I2PException, IOException
-    {
-        this(PrivDestination.fromBase64String(keyStr), props, i2p);
-    }
-    
-    /**
-     * (do not use this constructor directly)
-     */
-    
-    public I2PXmlRpcServer(PrivDestination privKey, Properties props, I2PAppContext i2p)
-        throws DataFormatException, I2PException
-    {
-        super();
-    
-        log = i2p.logManager().getLog(this.getClass());
-    
-        log.info("creating socket manager for key dest "
-            + privKey.getDestinationBase64().substring(0, 16)
-            + "...");
-    
-        // start by getting a socket manager
-        socketMgr = I2PSocketManagerFactory.createManager(privKey.getInputStream(), props);
-        if (socketMgr == null) {
-            throw new I2PException("Failed to create socketManager, maybe can't reach i2cp port");
-        }
-        
-        log.info("getting server socket, socketMgr="+socketMgr);
-    
-        // get a server socket
-        serverSocket = socketMgr.getServerSocket();
-    
-        log.info("got server socket, ready to run");
-    
-        dest = socketMgr.getSession().getMyDestination();
-        
-        log.info("full dest="+dest.toBase64());
-        System.out.println("full dest="+dest.toBase64());
-    
-    }
-    
-    /**
-     * Run this server within the current thread of execution.
-     * This function never returns. If you want to run the server
-     * in a background thread, use the .start() method instead.
-     */
-    
-    public void run()
-    {
-        log.info("run: listening for inbound XML-RPC requests...");
-    
-        while (true)
-        {
-            System.out.println("I2PXmlRpcServer.run: waiting for inbound XML-RPC I2P conn...");
-
-            try {
-                sessSocket = serverSocket.accept();
-
-                I2PXmlRpcServerWorkerThread sessThread = new I2PXmlRpcServerWorkerThread(sessSocket);
-                sessThread.start();
-
-                /**
-                System.out.println("I2PXmlRpcServer.run: got inbound XML-RPC I2P conn");
-
-                log.info("run: Got client connection, creating streams");
-    
-                InputStream socketIn = sessSocket.getInputStream();
-                OutputStreamWriter socketOut = new OutputStreamWriter(sessSocket.getOutputStream());
-    
-                log.info("run: reading http headers");
-    
-                // read headers, determine size of req
-                int size = readHttpHeaders(socketIn);
-    
-                if (size <= 0) {
-                    // bad news
-                    log.info("read req failed, terminating session");
-                    sessSocket.close();
-                    continue;
-                }
-    
-                log.info("run: reading request body of "+size+" bytes");
-
-                // get raw request body
-                byte [] reqBody = new byte[size];
-                for (int i=0; i<size; i++) {
-                    int b = socketIn.read();
-                    reqBody[i] = (byte)b;
-                }
-                //socketIn.read(reqBody);
-    
-                //log.info("reqBody='" + (new String(reqBody)) + "'");
-                //System.out.println("reqBody='" + (new String(reqBody)) + "'");
-                //System.out.println("reqBody:");
-                //for (int ii=0; ii<reqBody.length; ii++) {
-                //    System.out.println("i=" + ii + " ch="+reqBody[ii]);
-                //}
-    
-                ByteArrayInputStream reqBodyStream = new ByteArrayInputStream(reqBody);
-
-                log.info("run: executing request");
-                
-                System.out.println("run: executing request");
-                
-                // read and execute full request
-                byte [] result;
-                try {
-                    result = execute(reqBodyStream);
-                } catch (Exception e) {
-                    System.out.println("run: execute failed, closing socket");
-                    sessSocket.close();
-                    System.out.println("run: closed socket");
-                    throw e;
-                }
-    
-                log.info("run: sending response");
-    
-    
-                // fudge - manual header and response generation
-                socketOut.write(
-                    "HTTP/1.0 200 OK\r\n" +
-                    "Server: I2P XML-RPC server by aum\r\n" +
-                    "Date: " + (new Date().toString()) + "\r\n" +
-                    "Content-type: text/xml\r\n" +
-                    "Content-length: " + String.valueOf(result.length) + "\r\n" +
-                    "\r\n");
-                socketOut.write(new String(result));
-                socketOut.flush();
-    
-                log.info("closing socket");
-                System.out.println("closing socket");
-    
-                //response.setContentType ("text/xml");
-                //response.setContentLength (result.length());
-                //OutputStream out = response.getOutputStream();
-                //out.write (result);
-                //out.flush (); 
-    
-                sessSocket.close();
-    
-                log.info("session complete");
-                **/
-
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-    
-        }
-    
-    }
-    
-    /**
-     * Called as part of an incoming XML-RPC request,
-     * reads and parses http headers from input stream.
-     * @param in the InputStream of the socket connection from the
-     * currently connected client
-     * @return value of 'Content-Length' field, as the number of bytes
-     * expected in the body of the request, or -1 if the request headers
-     * are invalid
-     * @throws IOException
-     */
-    
-    protected int readHttpHeaders(InputStream in) throws IOException
-    {
-        int contentLength = -1;
-    
-        while (true) {
-            // read/parse one line
-            String line = readline(in);
-    
-            String [] flds = line.split(":\\s+", 2);
-            log.debug("after split: flds='"+flds+"'");
-    
-            String hdrKey = flds[0];
-    
-            if (flds.length == 1) {
-                // not an HTTP header
-                log.info("skipping non-header, hdrKey='"+hdrKey+"'");
-                continue;
-            }
-    
-            System.out.println("I2PXmlRpcServer: '"+flds[0]+"'='"+flds[1]+"'");
-
-            String hdrVal = flds[1];
-    
-            log.info("hdrKey='"+hdrKey+"', hdrVal='"+hdrVal+"'");
-            
-            if (hdrKey.equals("Content-Type")) {
-                if (!hdrVal.equals("text/xml")) {
-                    // barf - not text/xml content type
-                    return -1;
-                }
-            }
-    
-            if (hdrKey.equals("Content-Length")) {
-                // got our length now - done with headers
-                contentLength = new Integer(hdrVal).intValue();
-                break;
-            }
-        }
-    
-        log.info("Got content-length, now read remaining headers");
-        
-        // read and discard any remaining headers
-        while (true) {
-            String line = readline(in);
-            int lineLen = line.length();
-            log.info("line("+lineLen+")='"+line+"'");
-            System.out.println("Disccarding superflous header: '"+line+"'");
-            if (lineLen == 0) {
-                break;
-            }
-        }
-    
-        log.info("Content length is "+contentLength);
-        
-        return contentLength;
-    
-    }
-    
-    /**
-     * Called as part of an incoming XML-RPC request,
-     * reads and parses http headers from input stream.
-     * @param in the InputStream of the socket connection from the
-     * currently connected client
-     * @return the line read, as a string
-     * @throws IOException
-     */
-    
-    protected String readline(InputStream in) throws IOException
-    {
-        ByteArrayOutputStream os = new ByteArrayOutputStream();
-        
-        while (true) {
-            int ch = in.read();
-            switch (ch) {
-                case '\n':
-                case -1:
-                    String s = os.toString();
-                    log.debug("Got line '"+s+"'");
-                    return os.toString();
-                case '\r':
-                    break;
-                default:
-                    os.write(ch);
-            }
-        }
-    }
-    
-    /**
-     * Launches the server as a background thread.
-     * (To run within the calling thread, use the .run() method instead).
-     */
-    
-    public void start()
-    {
-        log.debug("Starting server as a thread");
-        serverThread = new Thread(this);
-        serverThread.start();
-    }
-
-    /**
-    public void stop()
-    {
-        if (serverThread != null) {
-            serverThread.stop();
-        }
-    }
-    **/
-
-}
-
-
-
diff --git a/apps/q/java/src/net/i2p/aum/I2PXmlRpcServerFactory.java b/apps/q/java/src/net/i2p/aum/I2PXmlRpcServerFactory.java
deleted file mode 100644
index 14fd092b51034a8bc1b6aba7d8dc3e4589b0828f..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/I2PXmlRpcServerFactory.java
+++ /dev/null
@@ -1,157 +0,0 @@
-package net.i2p.aum;
-
-import java.io.IOException;
-import java.util.Properties;
-
-import net.i2p.I2PAppContext;
-import net.i2p.I2PException;
-import net.i2p.client.streaming.I2PSocketManager;
-import net.i2p.data.DataFormatException;
-import net.i2p.util.Log;
-
-
-/**
- * Generates I2P-compatible XML-RPC server objects
- * (of class I2PXmlRpcServer). If you instead want to create 
- * instances of your own 
- *
- * Invoke this class from your shell to see a demo
- * @author aum
- */
-public class I2PXmlRpcServerFactory
-{
-    public I2PSocketManager socketManager;
-    
-    public Properties props;
-    
-    public static int defaultTunnelLength = 2;
-
-    // set to enable debugging msgs
-    public static boolean debug = false;
-    
-    public static Log log;
-    protected I2PAppContext i2p;
-    
-    // hostname/port of I2P router we're using
-    //public static String i2cpHost = "127.0.0.1";
-    //public static int i2cpPort = 7654;
-
-    /**
-     * Create an I2P XML-RPC server factory using default
-     * tunnel settings
-     */
-    public I2PXmlRpcServerFactory(I2PAppContext i2p)
-    {
-        // get a socket manager
-        this(defaultTunnelLength, defaultTunnelLength,
-             defaultTunnelLength, defaultTunnelLength, i2p);
-    }
-    
-    
-    /**
-     * Create an I2P XML-RPC server factory, using settings provided
-     * by arguments
-     * @param lengthIn The value of 'inbound.length' property
-     * @param lengthOut The value of 'outbound.length' property
-     * @param lengthVarianceIn Value of 'inbound.lengthVariance' property
-     * @param lengthVarianceOut Value of 'outbound.lengthVariance' property
-     * @param log an I2P logger
-     */
-    public I2PXmlRpcServerFactory(int lengthIn, int lengthOut,
-                                  int lengthVarianceIn, int lengthVarianceOut,
-                                  I2PAppContext i2p)
-    {
-        this.i2p = i2p;
-        log = i2p.logManager().getLog(this.getClass());
-    
-        // set up tunnel properties for server objects
-        props = new Properties();
-        props.setProperty("inbound.length", String.valueOf(lengthIn));
-        props.setProperty("outbound.length", String.valueOf(lengthOut));
-        props.setProperty("inbound.lengthVariance", String.valueOf(lengthVarianceIn));
-        props.setProperty("outbound.lengthVariance", String.valueOf(lengthVarianceIn));
-    }
-    
-    /**
-     * Creates a new I2PXmlRpcServer listening on a new randomly created destination
-     * @return a new I2PXmlRpcServer object, whose '.addHandler()' method you should
-     * invoke to add a handler object.
-     * @throws I2PException, IOException, DataFormatException
-     */
-    public I2PXmlRpcServer newServer() throws I2PException, IOException, DataFormatException
-    {
-        return newServer(PrivDestination.newKey());
-    }
-    
-    
-    /**
-     * Creates a new I2PXmlRpcServer listening on a given dest, from key provided
-     * as a base64 string
-     * @param keyStr base64 representation of full private key for the destination
-     * the server is to listen on
-     * @return a new I2PXmlRpcServer object
-     * @throws DataFormatException
-     */
-    public I2PXmlRpcServer newServer(String keyStr)
-        throws DataFormatException, I2PException, IOException
-    {
-        return newServer(PrivDestination.fromBase64String(keyStr));
-    }
-    
-    
-    /**
-     * Creates a new I2PXmlRpcServer listening on a given dest, from key provided
-     * as a PrivDestination object
-     * @param key a PrivDestination object representing the private destination
-     * the server is to listen on
-     * @return a new I2PXmlRpcServer object
-     * @throws DataFormatException
-     */
-    public I2PXmlRpcServer newServer(PrivDestination key) throws DataFormatException, I2PException
-    {
-        // main newServer
-        I2PXmlRpcServer server = new I2PXmlRpcServer(key, props, i2p);
-        server.debug = debug;
-        return server;
-    }
-
-    /**
-     * Demonstration of I2P XML-RPC server.
-     * Creates a server listening on a random destination, and writes the base64
-     * destination into a file called "demo.dest64".
-     *
-     * After launching this program from a command shell, you should
-     * launch I2PXmlRpcClientFactory from another command shell
-     * to execute the client side of the demo.
-     *
-     * This program accepts no arguments.
-     */
-    public static void main(String [] args)
-    {
-        debug = true;
-        I2PXmlRpcServer.debug = true;
-        
-        I2PAppContext i2p = new I2PAppContext();
-    
-        I2PXmlRpcServerFactory f = new I2PXmlRpcServerFactory(0,0,0,0, i2p);
-    
-        try {
-    
-            f.log.info("Creating new server on a new key");
-            I2PXmlRpcServer s = f.newServer();
-    
-            f.log.info("Creating and adding handler object");
-            I2PXmlRpcDemoClass demo = new I2PXmlRpcDemoClass();
-            s.addHandler("foo", demo);
-
-            f.log.info("Saving dest for this server in file 'demo.dest64'");
-            new SimpleFile("demo.dest64", "rws").write(s.dest.toBase64());
-
-            f.log.info("Running server (Press Ctrl-C to kill)");
-            s.run();
-    
-        } catch (Exception e) { e.printStackTrace(); }
-        
-    }
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/Mimetypes.java b/apps/q/java/src/net/i2p/aum/Mimetypes.java
deleted file mode 100644
index c783e89c1c23ccd53b887de648af0fb0f222c0ef..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/Mimetypes.java
+++ /dev/null
@@ -1,392 +0,0 @@
-package net.i2p.aum;
-
-/** 
- * creates a convenient map of file extensions <-> mimetypes
- */
-
-public class Mimetypes
-{
-    public static String [][] _map = {
-
-        { ".bz2", "application/x-bzip2" },
-        { ".csm", "application/cu-seeme" },
-        { ".cu", "application/cu-seeme" },
-        { ".tsp", "application/dsptype" },
-        { ".xls", "application/excel" },
-        { ".spl", "application/futuresplash" },
-        { ".hqx", "application/mac-binhex40" },
-        { ".doc", "application/msword" },
-        { ".dot", "application/msword" },
-        { ".bin", "application/octet-stream" },
-        { ".oda", "application/oda" },
-        { ".pdf", "application/pdf" },
-        { ".asc", "application/pgp-keys" },
-        { ".pgp", "application/pgp-signature" },
-        { ".ps", "application/postscript" },
-        { ".ai", "application/postscript" },
-        { ".eps", "application/postscript" },
-        { ".ppt", "application/powerpoint" },
-        { ".rtf", "application/rtf" },
-        { ".wp5", "application/wordperfect5.1" },
-        { ".zip", "application/zip" },
-        { ".wk", "application/x-123" },
-        { ".bcpio", "application/x-bcpio" },
-        { ".pgn", "application/x-chess-pgn" },
-        { ".cpio", "application/x-cpio" },
-        { ".deb", "application/x-debian-package" },
-        { ".dcr", "application/x-director" },
-        { ".dir", "application/x-director" },
-        { ".dxr", "application/x-director" },
-        { ".dvi", "application/x-dvi" },
-        { ".pfa", "application/x-font" },
-        { ".pfb", "application/x-font" },
-        { ".gsf", "application/x-font" },
-        { ".pcf", "application/x-font" },
-        { ".pcf.Z", "application/x-font" },
-        { ".gtar", "application/x-gtar" },
-        { ".tgz", "application/x-gtar" },
-        { ".hdf", "application/x-hdf" },
-        { ".phtml", "application/x-httpd-php" },
-        { ".pht", "application/x-httpd-php" },
-        { ".php", "application/x-httpd-php" },
-        { ".php3", "application/x-httpd-php3" },
-        { ".phps", "application/x-httpd-php3-source" },
-        { ".php3p", "application/x-httpd-php3-preprocessed" },
-        { ".class", "application/x-java" },
-        { ".latex", "application/x-latex" },
-        { ".frm", "application/x-maker" },
-        { ".maker", "application/x-maker" },
-        { ".frame", "application/x-maker" },
-        { ".fm", "application/x-maker" },
-        { ".fb", "application/x-maker" },
-        { ".book", "application/x-maker" },
-        { ".fbdoc", "application/x-maker" },
-        { ".mif", "application/x-mif" },
-        { ".nc", "application/x-netcdf" },
-        { ".cdf", "application/x-netcdf" },
-        { ".pac", "application/x-ns-proxy-autoconfig" },
-        { ".o", "application/x-object" },
-        { ".pl", "application/x-perl" },
-        { ".pm", "application/x-perl" },
-        { ".shar", "application/x-shar" },
-        { ".swf", "application/x-shockwave-flash" },
-        { ".swfl", "application/x-shockwave-flash" },
-        { ".sit", "application/x-stuffit" },
-        { ".sv4cpio", "application/x-sv4cpio" },
-        { ".sv4crc", "application/x-sv4crc" },
-        { ".tar", "application/x-tar" },
-        { ".gf", "application/x-tex-gf" },
-        { ".pk", "application/x-tex-pk" },
-        { ".PK", "application/x-tex-pk" },
-        { ".texinfo", "application/x-texinfo" },
-        { ".texi", "application/x-texinfo" },
-        { ".~", "application/x-trash" },
-        { ".%", "application/x-trash" },
-        { ".bak", "application/x-trash" },
-        { ".old", "application/x-trash" },
-        { ".sik", "application/x-trash" },
-        { ".t", "application/x-troff" },
-        { ".tr", "application/x-troff" },
-        { ".roff", "application/x-troff" },
-        { ".man", "application/x-troff-man" },
-        { ".me", "application/x-troff-me" },
-        { ".ms", "application/x-troff-ms" },
-        { ".ustar", "application/x-ustar" },
-        { ".src", "application/x-wais-source" },
-        { ".wz", "application/x-wingz" },
-        { ".au", "audio/basic" },
-        { ".snd", "audio/basic" },
-        { ".mid", "audio/midi" },
-        { ".midi", "audio/midi" },
-        { ".mpga", "audio/mpeg" },
-        { ".mpega", "audio/mpeg" },
-        { ".mp2", "audio/mpeg" },
-        { ".mp3", "audio/mpeg" },
-        { ".m3u", "audio/mpegurl" },
-        { ".aif", "audio/x-aiff" },
-        { ".aiff", "audio/x-aiff" },
-        { ".aifc", "audio/x-aiff" },
-        { ".gsm", "audio/x-gsm" },
-        { ".ra", "audio/x-pn-realaudio" },
-        { ".rm", "audio/x-pn-realaudio" },
-        { ".ram", "audio/x-pn-realaudio" },
-        { ".rpm", "audio/x-pn-realaudio-plugin" },
-        { ".wav", "audio/x-wav" },
-        { ".gif", "image/gif" },
-        { ".ief", "image/ief" },
-        { ".jpeg", "image/jpeg" },
-        { ".jpg", "image/jpeg" },
-        { ".jpe", "image/jpeg" },
-        { ".png", "image/png" },
-        { ".tiff", "image/tiff" },
-        { ".tif", "image/tiff" },
-        { ".ras", "image/x-cmu-raster" },
-        { ".bmp", "image/x-ms-bmp" },
-        { ".pnm", "image/x-portable-anymap" },
-        { ".pbm", "image/x-portable-bitmap" },
-        { ".pgm", "image/x-portable-graymap" },
-        { ".ppm", "image/x-portable-pixmap" },
-        { ".rgb", "image/x-rgb" },
-        { ".xbm", "image/x-xbitmap" },
-        { ".xpm", "image/x-xpixmap" },
-        { ".xwd", "image/x-xwindowdump" },
-        { ".csv", "text/comma-separated-values" },
-        { ".html", "text/html" },
-        { ".htm", "text/html" },
-        { ".mml", "text/mathml" },
-        { ".txt", "text/plain" },
-        { ".rtx", "text/richtext" },
-        { ".tsv", "text/tab-separated-values" },
-        { ".h++", "text/x-c++hdr" },
-        { ".hpp", "text/x-c++hdr" },
-        { ".hxx", "text/x-c++hdr" },
-        { ".hh", "text/x-c++hdr" },
-        { ".c++", "text/x-c++src" },
-        { ".cpp", "text/x-c++src" },
-        { ".cxx", "text/x-c++src" },
-        { ".cc", "text/x-c++src" },
-        { ".h", "text/x-chdr" },
-        { ".csh", "text/x-csh" },
-        { ".c", "text/x-csrc" },
-        { ".java", "text/x-java" },
-        { ".moc", "text/x-moc" },
-        { ".p", "text/x-pascal" },
-        { ".pas", "text/x-pascal" },
-        { ".etx", "text/x-setext" },
-        { ".sh", "text/x-sh" },
-        { ".tcl", "text/x-tcl" },
-        { ".tk", "text/x-tcl" },
-        { ".tex", "text/x-tex" },
-        { ".ltx", "text/x-tex" },
-        { ".sty", "text/x-tex" },
-        { ".cls", "text/x-tex" },
-        { ".vcs", "text/x-vCalendar" },
-        { ".vcf", "text/x-vCard" },
-        { ".dl", "video/dl" },
-        { ".fli", "video/fli" },
-        { ".gl", "video/gl" },
-        { ".mpeg", "video/mpeg" },
-        { ".mpg", "video/mpeg" },
-        { ".mpe", "video/mpeg" },
-        { ".qt", "video/quicktime" },
-        { ".mov", "video/quicktime" },
-        { ".asf", "video/x-ms-asf" },
-        { ".asx", "video/x-ms-asf" },
-        { ".avi", "video/x-msvideo" },
-        { ".movie", "video/x-sgi-movie" },
-        { ".vrm", "x-world/x-vrml" },
-        { ".vrml", "x-world/x-vrml" },
-        { ".wrl", "x-world/x-vrml" },
-
-    };
-
-    /**
-     * Attempts to determine a mimetype
-     * @param path - either a file extension string (containing the
-     * leading '.') or a full file pathname (in which case, the extension
-     * will be extracted).
-     * @return the mimetype that corresponds to the file extension, if the
-     * file extension is known, or "application/octet-stream" if the
-     * file extension is not known.
-     */
-    public static String guessType(String path) {
-        // rip the file extension from the path
-        // first - split 'directories', and get last part
-        String [] dirs = path.split("/");
-        String filename = dirs[dirs.length-1];
-        String [] bits = filename.split("\\.");
-        String extension = "." + bits[bits.length-1];
-        
-        // default mimetype applied to unknown file extensions
-        String type = "application/octet-stream";
-
-        for (int i=0; i<_map.length; i++) {
-            String [] rec = _map[i];
-            if (rec[0].equals(extension)) {
-                type = rec[1];
-                break;
-            }
-        }
-        return type;
-    }
-
-    /**
-     * Attempts to guess the file extension corresponding to a given
-     * mimetype.
-     * @param type a mimetype string
-     * @return a file extension commonly used for storing files of this type,
-     * or defaults to ".bin" if mimetype not known
-     */
-    public static String guessExtension(String type) {
-        // default extension applied to unknown mimetype
-        String extension = ".bin";
-        for (int i=0; i<_map.length; i++) {
-            String [] rec = _map[i];
-            if (rec[1].equals(type)) {
-                extension = rec[0];
-                break;
-            }
-        }
-        return extension;
-    }
-
-}
-
-/**
-
-suffix_map = {
-    '.tgz': '.tar.gz',
-    '.taz': '.tar.gz',
-    '.tz': '.tar.gz',
-    }
-
-encodings_map = {
-    '.gz': 'gzip',
-    '.Z': 'compress',
-    }
-
-# Before adding new types, make sure they are either registered with IANA, at
-# http://www.isi.edu/in-notes/iana/assignments/media-types
-# or extensions, i.e. using the x- prefix
-
-# If you add to these, please keep them sorted!
-types_map = {
-    '.a'      : 'application/octet-stream',
-    '.ai'     : 'application/postscript',
-    '.aif'    : 'audio/x-aiff',
-    '.aifc'   : 'audio/x-aiff',
-    '.aiff'   : 'audio/x-aiff',
-    '.au'     : 'audio/basic',
-    '.avi'    : 'video/x-msvideo',
-    '.bat'    : 'text/plain',
-    '.bcpio'  : 'application/x-bcpio',
-    '.bin'    : 'application/octet-stream',
-    '.bmp'    : 'image/x-ms-bmp',
-    '.c'      : 'text/plain',
-    # Duplicates :(
-    '.cdf'    : 'application/x-cdf',
-    '.cdf'    : 'application/x-netcdf',
-    '.cpio'   : 'application/x-cpio',
-    '.csh'    : 'application/x-csh',
-    '.css'    : 'text/css',
-    '.dll'    : 'application/octet-stream',
-    '.doc'    : 'application/msword',
-    '.dot'    : 'application/msword',
-    '.dvi'    : 'application/x-dvi',
-    '.eml'    : 'message/rfc822',
-    '.eps'    : 'application/postscript',
-    '.etx'    : 'text/x-setext',
-    '.exe'    : 'application/octet-stream',
-    '.gif'    : 'image/gif',
-    '.gtar'   : 'application/x-gtar',
-    '.h'      : 'text/plain',
-    '.hdf'    : 'application/x-hdf',
-    '.htm'    : 'text/html',
-    '.html'   : 'text/html',
-    '.ief'    : 'image/ief',
-    '.jpe'    : 'image/jpeg',
-    '.jpeg'   : 'image/jpeg',
-    '.jpg'    : 'image/jpeg',
-    '.js'     : 'application/x-javascript',
-    '.ksh'    : 'text/plain',
-    '.latex'  : 'application/x-latex',
-    '.m1v'    : 'video/mpeg',
-    '.man'    : 'application/x-troff-man',
-    '.me'     : 'application/x-troff-me',
-    '.mht'    : 'message/rfc822',
-    '.mhtml'  : 'message/rfc822',
-    '.mif'    : 'application/x-mif',
-    '.mov'    : 'video/quicktime',
-    '.movie'  : 'video/x-sgi-movie',
-    '.mp2'    : 'audio/mpeg',
-    '.mp3'    : 'audio/mpeg',
-    '.mpa'    : 'video/mpeg',
-    '.mpe'    : 'video/mpeg',
-    '.mpeg'   : 'video/mpeg',
-    '.mpg'    : 'video/mpeg',
-    '.ms'     : 'application/x-troff-ms',
-    '.nc'     : 'application/x-netcdf',
-    '.nws'    : 'message/rfc822',
-    '.o'      : 'application/octet-stream',
-    '.obj'    : 'application/octet-stream',
-    '.oda'    : 'application/oda',
-    '.p12'    : 'application/x-pkcs12',
-    '.p7c'    : 'application/pkcs7-mime',
-    '.pbm'    : 'image/x-portable-bitmap',
-    '.pdf'    : 'application/pdf',
-    '.pfx'    : 'application/x-pkcs12',
-    '.pgm'    : 'image/x-portable-graymap',
-    '.pl'     : 'text/plain',
-    '.png'    : 'image/png',
-    '.pnm'    : 'image/x-portable-anymap',
-    '.pot'    : 'application/vnd.ms-powerpoint',
-    '.ppa'    : 'application/vnd.ms-powerpoint',
-    '.ppm'    : 'image/x-portable-pixmap',
-    '.pps'    : 'application/vnd.ms-powerpoint',
-    '.ppt'    : 'application/vnd.ms-powerpoint',
-    '.ps'     : 'application/postscript',
-    '.pwz'    : 'application/vnd.ms-powerpoint',
-    '.py'     : 'text/x-python',
-    '.pyc'    : 'application/x-python-code',
-    '.pyo'    : 'application/x-python-code',
-    '.qt'     : 'video/quicktime',
-    '.ra'     : 'audio/x-pn-realaudio',
-    '.ram'    : 'application/x-pn-realaudio',
-    '.ras'    : 'image/x-cmu-raster',
-    '.rdf'    : 'application/xml',
-    '.rgb'    : 'image/x-rgb',
-    '.roff'   : 'application/x-troff',
-    '.rtx'    : 'text/richtext',
-    '.sgm'    : 'text/x-sgml',
-    '.sgml'   : 'text/x-sgml',
-    '.sh'     : 'application/x-sh',
-    '.shar'   : 'application/x-shar',
-    '.snd'    : 'audio/basic',
-    '.so'     : 'application/octet-stream',
-    '.src'    : 'application/x-wais-source',
-    '.sv4cpio': 'application/x-sv4cpio',
-    '.sv4crc' : 'application/x-sv4crc',
-    '.swf'    : 'application/x-shockwave-flash',
-    '.t'      : 'application/x-troff',
-    '.tar'    : 'application/x-tar',
-    '.tcl'    : 'application/x-tcl',
-    '.tex'    : 'application/x-tex',
-    '.texi'   : 'application/x-texinfo',
-    '.texinfo': 'application/x-texinfo',
-    '.tif'    : 'image/tiff',
-    '.tiff'   : 'image/tiff',
-    '.tr'     : 'application/x-troff',
-    '.tsv'    : 'text/tab-separated-values',
-    '.txt'    : 'text/plain',
-    '.ustar'  : 'application/x-ustar',
-    '.vcf'    : 'text/x-vcard',
-    '.wav'    : 'audio/x-wav',
-    '.wiz'    : 'application/msword',
-    '.xbm'    : 'image/x-xbitmap',
-    '.xlb'    : 'application/vnd.ms-excel',
-    # Duplicates :(
-    '.xls'    : 'application/excel',
-    '.xls'    : 'application/vnd.ms-excel',
-    '.xml'    : 'text/xml',
-    '.xpm'    : 'image/x-xpixmap',
-    '.xsl'    : 'application/xml',
-    '.xwd'    : 'image/x-xwindowdump',
-    '.zip'    : 'application/zip',
-    }
-
-# These are non-standard types, commonly found in the wild.  They will only
-# match if strict=0 flag is given to the API methods.
-
-# Please sort these too
-common_types = {
-    '.jpg' : 'image/jpg',
-    '.mid' : 'audio/midi',
-    '.midi': 'audio/midi',
-    '.pct' : 'image/pict',
-    '.pic' : 'image/pict',
-    '.pict': 'image/pict',
-    '.rtf' : 'application/rtf',
-    '.xul' : 'text/xul'
-    }
-**/
-
diff --git a/apps/q/java/src/net/i2p/aum/OOTest.java b/apps/q/java/src/net/i2p/aum/OOTest.java
deleted file mode 100644
index f7a6fffed9ee5dc492a0e2e1ae81d3a520c47c7c..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/OOTest.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package net.i2p.aum;
-
-
-public class OOTest
-{
-    public int add(int a, int b)
-    {
-      return (a + b);
-    }
-    
-    public static void main(String[] args)
-    {
-        OOTest mytest = new OOTest();
-        System.out.println(mytest.add(3,3));
-    }
-}
-
-
diff --git a/apps/q/java/src/net/i2p/aum/PrivDestination.java b/apps/q/java/src/net/i2p/aum/PrivDestination.java
deleted file mode 100644
index 2d09bb0af6772549c22bd69d84e1e503d2f66f3d..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/PrivDestination.java
+++ /dev/null
@@ -1,236 +0,0 @@
-
-package net.i2p.aum;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-import net.i2p.I2PException;
-import net.i2p.client.I2PClient;
-import net.i2p.client.I2PClientFactory;
-import net.i2p.data.Base64;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.DataStructureImpl;
-import net.i2p.data.Destination;
-import net.i2p.data.PrivateKey;
-import net.i2p.data.PublicKey;
-import net.i2p.data.SigningPrivateKey;
-import net.i2p.data.SigningPublicKey;
-import net.i2p.util.Log;
-
-/**
- * A convenience class for encapsulating and manipulating I2P private keys
- */
-
-public class PrivDestination
-    //extends ByteArrayInputStream
-    extends DataStructureImpl
-{
-    protected byte [] _bytes;
-
-    protected Destination _dest;
-    protected PrivateKey _privKey;
-    protected SigningPrivateKey _signingPrivKey;
-    
-    protected static Log _log;
-    
-    /**
-     * Create a PrivDestination object.
-     * In most cases, you'll probably want to skip this constructor,
-     * and create PrivDestination objects by invoking the desired static methods
-     * of this class.
-     * @param raw an array of bytes containing the raw binary private key
-     */    
-    public PrivDestination(byte [] raw) throws DataFormatException, IOException
-    {
-        //super(raw);
-        _log = new Log("PrivDestination");
-
-        _bytes = raw;
-        readBytes(getInputStream());
-    }
-
-    /**
-     * reconstitutes a PrivDestination from previously exported Base64
-     */
-    public PrivDestination(String b64) throws DataFormatException, IOException {
-        this(Base64.decode(b64));
-    }
-
-    /**
-     * generates a new PrivDestination with random keys
-     */
-    public PrivDestination() throws I2PException, IOException
-    {
-        I2PClient client = I2PClientFactory.createClient();
-    
-        ByteArrayOutputStream streamOut = new ByteArrayOutputStream();
-    
-        // create a dest
-        client.createDestination(streamOut);
-
-        _bytes = streamOut.toByteArray();
-        readBytes(getInputStream());
-        
-        // construct from the stream
-        //return new PrivDestination(streamOut.toByteArray());
-    }
-
-    /** return the public Destination object for this private dest */
-    public Destination getDestination() {
-        return _dest;
-    }
-
-    /** return a PublicKey (encryption public key) object for this priv dest */
-    public PublicKey getPublicKey() {
-        return getDestination().getPublicKey();
-    }
-
-    /** return a PrivateKey (encryption private key) object for this priv dest */
-    public PrivateKey getPrivateKey() {
-        return _privKey;
-    }
-
-    /** return a SigningPublicKey object for this priv dest */
-    public SigningPublicKey getSigningPublicKey() {
-        return getDestination().getSigningPublicKey();
-    }
-
-    /** return a SigningPrivateKey object for this priv dest */
-    public SigningPrivateKey getSigningPrivateKey() {
-        return _signingPrivKey;
-    }
-
-    // static methods returning an instance
-    
-    /**
-     * Creates a PrivDestination object
-     * @param base64 a string containing the base64 private key data
-     * @return a PrivDestination object encapsulating that key
-     */
-    public static PrivDestination fromBase64String(String base64)
-        throws DataFormatException, IOException
-    {
-        return new PrivDestination(Base64.decode(base64));
-    }
-    
-    /**
-     * Creates a PrivDestination object, from the base64 key data
-     * stored in a file.
-     * @param path the pathname of the file from which to read the base64 private key data
-     * @return a PrivDestination object encapsulating that key
-     */
-    public static PrivDestination fromBase64File(String path)
-        throws FileNotFoundException, IOException, DataFormatException
-    {
-        return fromBase64String(new SimpleFile(path, "r").read());
-        /*
-        File f = new File(path);
-        char [] rawchars = new char[(int)(f.length())];
-        byte [] rawbytes = new byte[(int)(f.length())];
-        FileReader fr = new FileReader(f);
-        fr.read(rawchars);
-        String raw64 = new String(rawchars);
-        return PrivDestination.fromBase64String(raw64);
-        */
-    }
-    
-    /**
-     * Creates a PrivDestination object, from the binary key data
-     * stored in a file.
-     * @param path the pathname of the file from which to read the binary private key data
-     * @return a PrivDestination object encapsulating that key
-     */
-    public static PrivDestination fromBinFile(String path)
-        throws FileNotFoundException, IOException, DataFormatException
-    {
-        byte [] raw = new SimpleFile(path, "r").readBytes();
-        return new PrivDestination(raw);
-    }
-    
-    /**
-     * Generate a new random I2P private key
-     * @return a PrivDestination object encapsulating that key
-     */
-    public static PrivDestination newKey() throws I2PException, IOException
-    {
-        return new PrivDestination();
-    }
-    
-    public ByteArrayInputStream getInputStream()
-    {
-        return new ByteArrayInputStream(_bytes);
-    }
-
-    /**
-     * Exports the key's full contents to a string
-     * @return A base64-format string containing the full contents
-     * of this private key. The string can be used in any subsequent
-     * call to the .fromBase64String static constructor method.
-     */
-/*
-    public String toBase64()
-    {
-        return Base64.encode(_bytes);
-    }
-*/
-
-    /**
-     * Exports the key's full contents to a byte array
-     * @return A byte array containing the full contents
-     * of this private key.
-     */
-/*
-    public byte [] toBytes()
-    {
-        return _bytes;
-    }
-*/
-
-    /**
-     * Converts this key to a public destination.
-     * @return a standard I2P Destination object containing the
-     * public portion of this private key.
-     */
-     /*
-    public Destination toDestination() throws DataFormatException
-    {
-        Destination dest = new Destination();
-        dest.readBytes(_bytes, 0);
-        return dest;
-    }
-    */
-
-    /**
-     * Converts this key to a base64 string representing a public destination
-     * @return a string containing a base64 representation of the destination
-     * corresponding to this private key.
-     */
-    public String getDestinationBase64() throws DataFormatException
-    {
-        return getDestination().toBase64();
-    }
-    
-    public void readBytes(java.io.InputStream strm)
-        throws net.i2p.data.DataFormatException, java.io.IOException
-    {
-        _dest = new Destination();
-        _privKey = new PrivateKey();
-        _signingPrivKey = new SigningPrivateKey();
-
-        _dest.readBytes(strm);
-        _privKey.readBytes(strm);
-        _signingPrivKey.readBytes(strm);
-    }
-    
-    public void writeBytes(java.io.OutputStream outputStream)
-        throws net.i2p.data.DataFormatException, java.io.IOException
-    {
-        _dest.writeBytes(outputStream);
-        _privKey.writeBytes(outputStream);
-        _signingPrivKey.writeBytes(outputStream);
-    }
-    
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/PropertiesFile.java b/apps/q/java/src/net/i2p/aum/PropertiesFile.java
deleted file mode 100644
index 2d1891549aff4702ed8b3ed3c3b6fde023a9d2ce..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/PropertiesFile.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * PropertiesFile.java
- *
- * Created on 20 March 2005, 19:30
- */
-
-package net.i2p.aum;
-
-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.Enumeration;
-import java.util.Hashtable;
-import java.util.NoSuchElementException;
-import java.util.Properties;
-
-/**
- * builds on Properties with methods to load/save directly to/from file
- */
-public class PropertiesFile extends Properties {
-    
-    public String _path;
-    public File _file;
-    public boolean _fileExists;
-
-    /**
-     * Creates a new instance of PropertiesFile
-     * @param path Absolute pathname of file where properties are to be stored
-     */
-    public PropertiesFile(String path) throws IOException {
-        super();
-        _path = path;
-        _file = new File(path);
-        _fileExists = _file.isFile();
-
-        if (_file.canRead()) {
-            loadFromFile();
-        }
-    }
-
-    /**
-     * Creates new PropertiesFile, updating its content with the
-     * keys/values in given hashtable
-     * @param path absolute pathname where properties file is located in filesystem
-     * @param h instance of Hashtable (or subclass). its content 
-     * will be written to this object (note that string representations of keys/vals
-     * will be used)
-     */
-    public PropertiesFile(String path, Hashtable h) throws IOException
-    {
-        this(path);
-        Enumeration keys = h.keys();
-        Object key;
-        while (true)
-        {
-            try {
-                key = keys.nextElement();
-            } catch (NoSuchElementException e) {
-                break;
-            }
-            setProperty(key.toString(), h.get(key).toString());
-        }
-    }
-    
-    /**
-     * Loads this object from the file
-     */
-    public void loadFromFile() throws IOException, FileNotFoundException {
-        if (_file.canRead()) {
-            InputStream fis = new FileInputStream(_file);
-            load(fis);
-        }
-    }
-
-    /**
-     * Saves this object to the file
-     */
-    public void saveToFile() throws IOException, FileNotFoundException {
-
-        if (!_fileExists) {
-            _file.createNewFile();
-            _fileExists = true;
-        }
-        OutputStream fos = new FileOutputStream(_file);
-        store(fos, null);
-    }
-    
-    /**
-     * Stores attribute
-     */
-    public Object setProperty(String key, String value) {
-        Object o = super.setProperty(key, value);
-        try {
-            saveToFile();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        return o;
-    }
-    
-    /**
-     * return a property as an int, fall back on default if not found or invalid
-     */
-    public int getIntProperty(String key, int dflt) {
-        try {
-            return new Integer((String)getProperty(key)).intValue();
-        } catch (Exception e) {
-            setIntProperty(key, dflt);
-            return dflt;
-        }
-    }
-
-    /**
-     * return a property as an int
-     */
-    public int getIntProperty(String key) {
-        return new Integer((String)getProperty(key)).intValue();
-    }
-
-    /**
-     * set a property as an int
-     */
-    public void setIntProperty(String key, int value) {
-        setProperty(key, String.valueOf(value));
-    }
-
-    /**
-     * return a property as a long, fall back on default if not found or invalid
-     */
-    public long getIntProperty(String key, long dflt) {
-        try {
-            return new Long((String)getProperty(key)).longValue();
-        } catch (Exception e) {
-            setLongProperty(key, dflt);
-            return dflt;
-        }
-    }
-
-    /**
-     * return a property as an int
-     */
-    public long getLongProperty(String key) {
-        return new Long((String)getProperty(key)).longValue();
-    }
-
-    /**
-     * set a property as an int
-     */
-    public void setLongProperty(String key, long value) {
-        setProperty(key, String.valueOf(value));
-    }
-
-    /**
-     * return a property as a float
-     */
-    public double getFloatProperty(String key) {
-        return new Float((String)getProperty(key)).floatValue();
-    }
-
-    /**
-     * return a property as a float, fall back on default if not found or invalid
-     */
-    public double getFloatProperty(String key, float dflt) {
-        try {
-            return new Float((String)getProperty(key)).floatValue();
-        } catch (Exception e) {
-            setFloatProperty(key, dflt);
-            return dflt;
-        }
-    }
-
-    /**
-     * set a property as a float
-     */
-    public void setFloatProperty(String key, float value) {
-        setProperty(key, String.valueOf(value));
-    }
-
-    /**
-     * return a property as a double
-     */
-    public double getDoubleProperty(String key) {
-        return new Double((String)getProperty(key)).doubleValue();
-    }
-
-    /**
-     * return a property as a double, fall back on default if not found
-     */
-    public double getDoubleProperty(String key, double dflt) {
-        try {
-            return new Double((String)getProperty(key)).doubleValue();
-        } catch (Exception e) {
-            setDoubleProperty(key, dflt);
-            return dflt;
-        }
-    }
-
-    /**
-     * set a property as a double
-     */
-    public void setDoubleProperty(String key, double value) {
-        setProperty(key, String.valueOf(value));
-    }
-
-    /**
-     * increment an integer property value
-     */
-    public void incrementIntProperty(String key) {
-        setIntProperty(key, getIntProperty(key)+1);
-    }
-
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/SimpleFile.java b/apps/q/java/src/net/i2p/aum/SimpleFile.java
deleted file mode 100644
index a6f0438deb0b79430b0741ff76529e174c56938c..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/SimpleFile.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package net.i2p.aum;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-
-/**
- * SimpleFile - subclass of File which adds some python-like
- * methods. Cuts out a lot of the red tape involved with reading
- * from and writing to files
- */
-public class SimpleFile {
-
-    public RandomAccessFile _file;
-    public String _path;
-
-    public SimpleFile(String path, String mode) throws FileNotFoundException {
-
-        _path = path;
-        _file = new RandomAccessFile(path, mode);
-    }
-    
-    public byte [] readBytes() throws IOException {
-        return readBytes((int)_file.length());
-    }
-
-    public byte[] readBytes(int n) throws IOException {
-        byte [] buf = new byte[n];
-        _file.readFully(buf);
-        return buf;
-    }
-
-    public char [] readChars() throws IOException {
-        return readChars((int)_file.length());
-    }
-
-    public char[] readChars(int n) throws IOException {
-        char [] buf = new char[n];
-        //_file.readFully(buf);
-        return buf;
-    }
-
-    /**
-     * Reads all remaining content from the file
-     * @return the content as a String
-     * @throws IOException
-     */
-    public String read() throws IOException {
-
-        return read((int)_file.length());
-    }
-    
-    /**
-     * Reads one or more bytes of data from the file
-     * @return the content as a String
-     * @throws IOException
-     */
-    public String read(int nbytes) throws IOException {
-    
-        return new String(readBytes(nbytes));
-    }
-    
-    /**
-     * Writes one or more bytes of data to a file
-     * @param buf a String containing the data to write
-     * @return the number of bytes written, as an int
-     * @throws IOException
-     */
-    public int write(String buf) throws IOException {
-
-        return write(buf.getBytes());
-    }
-
-    public int write(byte [] buf) throws IOException {
-
-        _file.write(buf);
-        return buf.length;
-    }
-
-    /**
-     * convenient one-hit write
-     * @param path pathname of file to write to
-     * @param buf data to write
-     */
-    public static int write(String path, String buf) throws IOException {
-        return new SimpleFile(path, "rws").write(buf);
-    }
-
-    /**
-     * tests if argument refers to an actual file
-     * @param path pathname to test
-     * @return true if a file, false if not
-     */
-    public boolean isFile() {
-        return new File(_path).isFile();
-    }
-    
-    /**
-     * tests if argument refers to a directory
-     * @param path pathname to test
-     * @return true if a directory, false if not
-     */
-    public boolean isDir() {
-        return new File(_path).isDirectory();
-    }
-    
-    /**
-     * tests if a file or directory exists
-     * @param path pathname to test
-     * @return true if exists, or false
-     */
-    public boolean exists() {
-        return new File(_path).exists();
-    }
-    
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/SimpleFile_old.java b/apps/q/java/src/net/i2p/aum/SimpleFile_old.java
deleted file mode 100644
index ac74b58ac29909ae64edcdfe2195bfbd145d06c5..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/SimpleFile_old.java
+++ /dev/null
@@ -1,121 +0,0 @@
-package net.i2p.aum;
-
-import java.io.File;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-
-/**
- * SimpleFile - subclass of File which adds some python-like
- * methods. Cuts out a lot of the red tape involved with reading
- * from and writing to files
- */
-public class SimpleFile_old extends File {
-
-    public FileReader _reader;
-    public FileWriter _writer;
-    
-    public SimpleFile_old(String path) {
-    
-        super(path);
-        
-        _reader = null;
-        _writer = null;
-    }
-    
-    /**
-     * Reads all remaining content from the file
-     * @return the content as a String
-     * @throws IOException
-     */
-    public String read() throws IOException {
-    
-        return read((int)length());
-    }
-    
-    /**
-     * Reads one or more bytes of data from the file
-     * @return the content as a String
-     * @throws IOException
-     */
-    public String read(int nbytes) throws IOException {
-    
-        // get a reader, if we don't already have one
-        if (_reader == null) {
-            _reader = new FileReader(this);
-        }
-    
-        char [] cbuf = new char[nbytes];
-    
-        int nread = _reader.read(cbuf);
-    
-        if (nread == 0) {
-            return "";
-        }
-    
-        return new String(cbuf, 0, nread);
-            
-    }
-    
-    /**
-     * Writes one or more bytes of data to a file
-     * @param buf a String containing the data to write
-     * @return the number of bytes written, as an int
-     * @throws IOException
-     */
-    public int write(String buf) throws IOException {
-    
-        // get a reader, if we don't already have one
-        if (_writer == null) {
-            _writer = new FileWriter(this);
-        }
-    
-        _writer.write(buf);
-        _writer.flush();
-        return buf.length();
-    }
-
-    public int write(byte [] buf) throws IOException {
-
-        return write(new String(buf));
-    }
-
-    /**
-     * convenient one-hit write
-     * @param path pathname of file to write to
-     * @param buf data to write
-     */
-    public static int write(String path, String buf) throws IOException {
-        SimpleFile_old f = new SimpleFile_old(path);
-        return f.write(buf);
-    }
-
-    /**
-     * tests if argument refers to an actual file
-     * @param path pathname to test
-     * @return true if a file, false if not
-     */
-    public static boolean isFile(String path) {
-        return new File(path).isFile();
-    }
-    
-    /**
-     * tests if argument refers to a directory
-     * @param path pathname to test
-     * @return true if a directory, false if not
-     */
-    public static boolean isDir(String path) {
-        return new File(path).isDirectory();
-    }
-    
-    /**
-     * tests if a file or directory exists
-     * @param path pathname to test
-     * @return true if exists, or false
-     */
-    public static boolean exists(String path) {
-        return new File(path).exists();
-    }
-    
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/SimpleQueue.java b/apps/q/java/src/net/i2p/aum/SimpleQueue.java
deleted file mode 100644
index 15da498ecd5238ad3000ae9bb1db0dd4930f8ba0..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/SimpleQueue.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * SimpleQueue.java
- *
- * Created on March 24, 2005, 11:14 PM
- */
-
-package net.i2p.aum;
-
-import java.util.Vector;
-
-/**
- * Implements simething similar to python's 'Queue' class
- */
-public class SimpleQueue {
-
-    public Vector items;
-
-    /** Creates a new instance of SimpleQueue */
-    public SimpleQueue() {
-        items = new Vector();
-    }
-
-    /**
-     * fetches the item at head of queue, blocking if queue is empty
-     */
-    public synchronized Object get()
-    {
-        while (true)
-        {
-            try {
-                if (items.size() == 0)
-                wait();
-        
-                // someone has added
-                Object item = items.get(0);
-                items.remove(0);
-                return item;
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-    /**
-     * adds a new object to the queue
-     */
-    public synchronized void put(Object item)
-    {
-        items.addElement(item);
-        notify();
-    }
-
-    private static class TestThread extends Thread {
-        
-        String id;
-        
-        SimpleQueue q;
-        
-        public TestThread(String id, SimpleQueue q) {
-            this.id = id;
-            this.q = q;
-        }
-        
-        public void run() {
-            try {
-                print("waiting for queue");
-
-                Object item = q.get();
-                
-                print("got item: '"+item+"'");
-                
-            } catch (Exception e) {
-                e.printStackTrace();
-                return;
-            }
-        }
-        
-        public void print(String msg) {
-            System.out.println("thread '"+id+"': "+msg);
-        }
-        
-    }
-
-    /**
-     * @param args the command line arguments
-     */
-    public static void main(String[] args) {
-
-        int i;
-        int nthreads = 7;
-
-        Thread [] threads = new Thread[nthreads];
-
-        SimpleQueue q = new SimpleQueue();
-
-        // populate the queue with some stuff
-        q.put("red");
-        q.put("orange");
-        q.put("yellow");
-        
-        // populate threads array
-        for (i = 0; i < nthreads; i++) {
-            threads[i] = new TestThread("thread"+i, q);
-        }
-        
-        // and launch the threads
-        for (i = 0; i < nthreads; i++) {
-            threads[i].start();
-        }
-
-        try {
-            Thread.sleep(3000);
-        } catch (Exception e) {
-            e.printStackTrace();
-            return;
-        }
-
-        // wait a bit and see what happens
-        String [] items = {"green", "blue", "indigo", "violet", "black", "white", "brown"};
-        for (i = 0; i < items.length; i++) {
-            String item = items[i];
-            System.out.println("main: adding '"+item+"'...");
-            q.put(item);
-            try {
-                Thread.sleep(3000);
-            } catch (Exception e) {
-                e.printStackTrace();
-                return;
-            }
-        }
-            
-        System.out.println("main: terminating");
-
-    }
-    
-}
diff --git a/apps/q/java/src/net/i2p/aum/SimpleSemaphore.java b/apps/q/java/src/net/i2p/aum/SimpleSemaphore.java
deleted file mode 100644
index 83a736acd67cfed5ff08fabbc0bcdf903583a6bd..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/SimpleSemaphore.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * SimpleSemaphore.java
- *
- * Created on March 24, 2005, 11:51 PM
- */
-
-package net.i2p.aum;
-
-/**
- * Simple implementation of semaphores
- */
-public class SimpleSemaphore {
-
-    protected int count;
-
-    /** Creates a new instance of SimpleSemaphore */
-    public SimpleSemaphore(int size) {
-        count = size;
-    }
-
-    public synchronized void acquire() throws InterruptedException
-    {
-        if (count == 0)
-        {
-            wait();
-        }
-        count -= 1;
-    }
-
-    public synchronized void release()
-    {
-        count += 1;
-        notify();
-    }
-    
-    private static class TestThread extends Thread
-    {
-        String id;
-        SimpleSemaphore sem;
-        
-        public TestThread(String id, SimpleSemaphore sem)
-        {
-            this.id = id;
-            this.sem = sem;
-        }
-        
-        public void run()
-        {
-            try {
-                print("waiting for semaphore");
-                sem.acquire();
-
-                print("got semaphore");
-
-                Thread.sleep(1000);
-            
-                print("releasing semaphore");
-            
-                sem.release();
-
-                print("terminating");
-
-            } catch (Exception e) {
-                e.printStackTrace();
-                return;
-            }
-        }
-        
-        public void print(String msg) {
-            System.out.println("thread '"+id+"': "+msg);
-        }
-    }
-
-    /**
-     * @param args the command line arguments
-     */
-    public static void main(String[] args) {
-
-        int i;
-        
-        Thread [] threads = new Thread[10];
-
-        SimpleSemaphore sem = new SimpleSemaphore(3);
-        
-        // populate threads array
-        for (i = 0; i < 10; i++) {
-            threads[i] = new TestThread("thread"+i, sem);
-        }
-        
-        // and launch the threads
-        for (i = 0; i < 10; i++) {
-            threads[i].start();
-        }
-
-        // wait a bit and see what happens
-        System.out.println("main: threads launched, waiting 20 secs");
-        
-        try {
-            Thread.sleep(20000);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-        System.out.println("main: terminating");
-
-    }
-    
-}
diff --git a/apps/q/java/src/net/i2p/aum/helloworld.java b/apps/q/java/src/net/i2p/aum/helloworld.java
deleted file mode 100644
index 2a8ce30e90375dbd5e733a84c80063da056fa04d..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/helloworld.java
+++ /dev/null
@@ -1,17 +0,0 @@
-
-public class helloworld
-{
-    public static void main(String [] args)
-    {
-        helloworld h = new helloworld();
-        h.greet();
-    }
-
-    public void greet()
-    {
-        System.out.println("Hi, this is your greeting");
-    }
-}
-
-
-
diff --git a/apps/q/java/src/net/i2p/aum/http/HtmlPage.java b/apps/q/java/src/net/i2p/aum/http/HtmlPage.java
deleted file mode 100644
index 67ede7207c173c9f095b14e41d76dd0b2c30dc2d..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/http/HtmlPage.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * HtmlPage.java
- *
- * Created on April 8, 2005, 8:22 PM
- */
-
-package net.i2p.aum.http;
-
-import java.util.Enumeration;
-
-import net.i2p.aum.DupHashtable;
-
-/**
- * Framework for building up a page of HTML by method calls alone, breaking
- * every design rule by enmeshing content, presentation and logic
- */
-public class HtmlPage {
-
-    public String dtd = "<!DOCTYPE HTML PUBLIC "
-        +"\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
-        +"\"http://www.w3.org/TR/html4/loose.dtd\">";
-
-    public Tag page;
-    public Tag head;
-    public Tag body;
-    DupHashtable cssSettings;
-    
-    /** Creates a new HtmlPage object */
-    public HtmlPage() {
-        page = new Tag("html");
-        head = new Tag(page, "head");
-        body = new Tag(page, "body");
-        cssSettings = new DupHashtable();
-    }
-
-    /** renders out the whole page into a single string */
-    public String toString() {
-
-        // embed stylesheet, if non-empty
-        if (cssSettings.size() > 0) {
-            Tag t1 = head.nest("style type=\"text/css\"");
-            t1.raw("<!--\n");
-            Tag cssTag = t1.nest();
-            t1.raw("-->\n");
-            Enumeration elems = cssSettings.keys();
-            while (elems.hasMoreElements()) {
-                String name = (String)elems.nextElement();
-                cssTag.raw(name + " { ");
-                Enumeration items = cssSettings.get(name).elements();
-                while (items.hasMoreElements()) {
-                    String item = (String)items.nextElement();
-                    cssTag.raw(item+";");
-                }
-                cssTag.raw(" }\n");
-            }
-        }
-
-        // now render out the whole page
-        return dtd + "\n" + page;
-    }
-
-    /** adds a setting to the page's embedded stylesheet */
-    public HtmlPage css(String tag, String item, String val) {
-        return css(tag, item+":"+val);
-    }
-    
-    /** adds a setting to the page's embedded stylesheet */
-    public HtmlPage css(String tag, String setting) {
-        cssSettings.put(tag, setting);
-        return this;
-    }
-}
diff --git a/apps/q/java/src/net/i2p/aum/http/I2PHttpRequestHandler.java b/apps/q/java/src/net/i2p/aum/http/I2PHttpRequestHandler.java
deleted file mode 100644
index 5ab1911be18d0f306bff68dcc09edd3341d76b80..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/http/I2PHttpRequestHandler.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * I2PHttpRequestHandler.java
- *
- * Created on April 8, 2005, 11:57 PM
- */
-
-package net.i2p.aum.http;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-
-import net.i2p.client.streaming.I2PSocket;
-
-/**
- *
- * @author  david
- */
-public abstract class I2PHttpRequestHandler extends MiniHttpRequestHandler
-{
-    /** Creates a new instance of I2PHttpRequestHandler */
-    public I2PHttpRequestHandler(MiniHttpServer server, Object sock, Object arg) 
-        throws Exception
-    {
-        super(server, sock, arg);
-    }
-
-    /** Extracts a readable InputStream from own socket */
-    public InputStream getInputStream() throws IOException {
-        try {
-            return ((I2PSocket)socket).getInputStream();
-        } catch (Exception e) {
-            return ((Socket)socket).getInputStream();
-        }
-    }
-    
-    /** Extracts a writeable OutputStream from own socket */
-    public OutputStream getOutputStream() throws IOException {
-        try {
-            return ((I2PSocket)socket).getOutputStream();
-        } catch (Exception e) {
-            return ((Socket)socket).getOutputStream();
-        }
-    }
-
-}
diff --git a/apps/q/java/src/net/i2p/aum/http/I2PHttpServer.java b/apps/q/java/src/net/i2p/aum/http/I2PHttpServer.java
deleted file mode 100644
index 0e693dac0de9a31a294f29973e9af1491a0e93a3..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/http/I2PHttpServer.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * I2PHttpServer.java
- *
- * Created on April 8, 2005, 11:39 PM
- */
-
-package net.i2p.aum.http;
-
-import java.io.IOException;
-import java.util.Properties;
-
-import net.i2p.I2PException;
-import net.i2p.aum.PrivDestination;
-import net.i2p.client.streaming.I2PServerSocket;
-import net.i2p.client.streaming.I2PSocket;
-import net.i2p.client.streaming.I2PSocketManager;
-import net.i2p.client.streaming.I2PSocketManagerFactory;
-import net.i2p.data.DataFormatException;
-
-/**
- *
- * @author  david
- */
-public class I2PHttpServer extends MiniHttpServer {
-
-    PrivDestination privKey;
-    I2PSocketManager socketMgr;
-
-    public I2PHttpServer(PrivDestination key)
-        throws DataFormatException, IOException, I2PException
-    {
-        this(key, I2PHttpRequestHandler.class, null, null);
-    }
-
-    public I2PHttpServer(PrivDestination key, Class hdlrClass)
-        throws DataFormatException, IOException, I2PException
-    {
-        this(key, hdlrClass, null, null);
-    }
-
-    public I2PHttpServer(PrivDestination key, Class hdlrClass, Properties props)
-        throws DataFormatException, IOException, I2PException
-    {
-        this(key, hdlrClass, null, props);
-    }
-
-    /** Creates a new instance of I2PHttpServer */
-    public I2PHttpServer(PrivDestination key, Class hdlrClass, Object hdlrArg, Properties props)
-        throws DataFormatException, IOException, I2PException
-    {
-        super(hdlrClass, hdlrArg);
-
-        if (key != null) {
-            privKey = key;
-        } else {
-            privKey = new PrivDestination();
-        }
-        
-        // get a socket manager
-    //    socketManager = I2PSocketManagerFactory.createManager(key);
-        if (props == null) {
-            socketMgr = I2PSocketManagerFactory.createManager(privKey.getInputStream());
-        } else {
-            socketMgr = I2PSocketManagerFactory.createManager(privKey.getInputStream(), props);
-        }
-
-        if (socketMgr == null) {
-            throw new I2PException("I2PHttpServer: Failed to create socketManager");
-        }
-
-        String d = privKey.getDestination().toBase64();
-        System.out.println("Server: getting server socket for dest "+d);
-    
-        // get a server socket
-        //serverSocket = socketManager.getServerSocket();
-    }
-    
-    public void getServerSocket() throws IOException {
-
-        I2PServerSocket sock;
-        sock = socketMgr.getServerSocket();
-        serverSocket = sock;
-        System.out.println("listening on dest: "+privKey.getDestination().toBase64());
-    }
-
-    /**
-     * Listens on our 'serverSocket' object for an incoming connection,
-     * and returns a connected socket object. You should override this
-     * if you're using non-standard socket objects
-     */
-    public Object acceptConnection() throws IOException {
-
-        I2PSocket sock;
-
-        try {
-            sock = ((I2PServerSocket)serverSocket).accept();
-        } catch (I2PException e) {
-            throw new IOException(e.toString());
-        }
-        
-        System.out.println("Got connection from: "+sock.getPeerDestination().toBase64());
-
-        //System.out.println("New connection accepted" +
-        //                    sock.getInetAddress() +
-        //                    ":" + sock.getPort());
-        return sock;
-    }
-
-    public static void main(String [] args) {
-        try {
-            System.out.println("I2PHttpServer: starting up with new random key");
-            I2PHttpServer server = new I2PHttpServer((PrivDestination)null);
-            System.out.println("I2PHttpServer: running server");
-            server.run();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/http/MiniDemoXmlRpcHandler.java b/apps/q/java/src/net/i2p/aum/http/MiniDemoXmlRpcHandler.java
deleted file mode 100644
index f75dfa94e65793ad8cb5ce4125752128605c116f..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/http/MiniDemoXmlRpcHandler.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * MiniDemoXmlRpcHandler.java
- *
- * Created on April 13, 2005, 3:20 PM
- */
-
-package net.i2p.aum.http;
-
-
-public class MiniDemoXmlRpcHandler {
-
-    MiniHttpServer server;
-    
-    public MiniDemoXmlRpcHandler(MiniHttpServer server) {
-        this.server = server;
-    }
-
-    public String bar(String arg) {
-        return "bar: got '"+arg+"'";
-    }
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/http/MiniHttpRequestHandler.java b/apps/q/java/src/net/i2p/aum/http/MiniHttpRequestHandler.java
deleted file mode 100644
index 42317a39a44214194071310673417a6cc2effd17..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/http/MiniHttpRequestHandler.java
+++ /dev/null
@@ -1,574 +0,0 @@
-/*
- * MiniHttpRequestHandler.java
- * Adapted from pont.net's httpRequestHandler (httpServer.java)
- *
- * Created on April 8, 2005, 3:15 PM
- */
-
-package net.i2p.aum.http;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.Socket;
-import java.net.URLDecoder;
-import java.util.Enumeration;
-import java.util.Vector;
-
-import net.i2p.aum.DupHashtable;
-import net.i2p.aum.Mimetypes;
-
-public abstract class MiniHttpRequestHandler implements Runnable {
-    final static String CRLF = "\r\n";
-
-    /** server which created this handler */
-    protected MiniHttpServer server;
-
-    /** socket through which client is connected to us */
-    protected Object socket;
-
-    /** stored constructor arg */
-    protected Object serverArg;
-
-    /** input sent from client in request */
-    protected InputStream input;
-
-    /** we use this to read from client */
-    protected BufferedReader br;
-
-    /** output sent to client in reply */
-    protected OutputStream output;
-
-    /** http request type - GET, POST etc */
-    protected String reqType;
-
-    /** the request pathname */
-    protected String reqFile;
-
-    /** the request protocol (eg 'HTTP/1.0') */
-    protected String reqProto;
-
-    /** http headers */
-    protected DupHashtable headerVars;
-    
-    /** variable settings from POST data */
-    public DupHashtable postVars;
-
-    /** variable settings from URL (?name1=val1&name2=val2...) */
-    public DupHashtable urlVars;
-
-    /** consolidated variable settings from URL or POST data */
-    public DupHashtable allVars;
-    
-    /** first line of response we send back to client, set this
-     * with 'setStatus'
-     */
-    private String status = "HTTP/1.0 200 OK";
-    private String contentType = "text/plain";
-    private String reqContentType = null;
-    protected String serverName = "aum's MiniHttpServer";
-    
-    protected byte [] rawContentBytes = null;
-    
-    /**
-     * raw data sent by client in post req
-     */
-    protected char [] postData;
-
-    /** if a POST, this holds the full POST data as a string */
-    public String postDataStr;
-
-    // Constructors
-    public MiniHttpRequestHandler(MiniHttpServer server, Object socket) throws Exception {
-        this(server, socket, null);
-    }
-
-    public MiniHttpRequestHandler(MiniHttpServer server, Object socket, Object arg) throws Exception {
-        this.server = server;
-        this.socket = socket;
-        this.serverArg = arg;
-        this.input = getInputStream();
-        this.output = getOutputStream();
-        this.br = new BufferedReader(new InputStreamReader(input));
-    }
-
-    // -------------------------------------------
-    // START OF OVERRIDEABLES
-    // -------------------------------------------
-    
-    // override these methods in subclass if your socket-type thang is not
-    // a genuine Socket objct
-
-    /** Extracts a readable InputStream from own socket */
-    public InputStream getInputStream() throws IOException {
-        return ((Socket)socket).getInputStream();
-    }
-    
-    /** Extracts a writeable OutputStream from own socket */
-    public OutputStream getOutputStream() throws IOException {
-        return ((Socket)socket).getOutputStream();
-    }
-
-    /** closes the socket (or our socket-ish object) */
-    public void closeSocket() throws IOException {
-        ((Socket)socket).close();
-    }
-
-    /** method which gets called upon receipt of a GET.
-     * You should override this
-     */
-    public abstract void on_GET() throws Exception;
-    
-    /** method which gets called upon receipt of a POST.
-     * You should override this
-     */
-    public abstract void on_POST() throws Exception;
-
-    // -------------------------------------------
-    // END OF OVERRIDEABLES
-    // -------------------------------------------
-
-    /** Sets the HTTP status line (default 'HTTP/1.0 200 OK') */
-    public void setStatus(String status) {
-        this.status = status;
-    }
-
-    /** Sets the Content=Type header (default "text/plain") */
-    public void setContentType(String contentType) {
-        this.contentType = contentType;
-    }
-
-    /** Sets the 'Server' header (default "aum's MiniHttpServer") */
-    public void setServer(String serverType) {
-        this.serverName = serverType;
-    }
-
-    /** Sets the full body of raw output to be written, replacing
-     * the generated html tags
-     */
-    public void setRawOutput(String raw) {
-        setRawOutput(raw.getBytes());
-    }
-    
-    /** Sets the full body of raw output to be written, replacing
-     * the generated html tags
-     */
-    public void setRawOutput(byte [] raw) {
-        rawContentBytes = raw;
-    }
-
-    /** writes a String to output - normally you shouldn't need to call
-     * this directly 
-     */
-    public void write(String raw) {
-        write(raw.getBytes());
-    }
-
-    /** writes a byte array to output - normally you shouldn't need to call
-     * this directly 
-     */
-    public void write(byte [] raw) {
-        try {
-            output.write(raw);
-        } catch (Exception e) {
-            System.out.print(e);
-        }
-    }
-
-    /** processes the request, sends back response */
-    public void run() {
-        try {
-            processRequest();
-        }
-        catch(Exception e) {
-            e.printStackTrace();
-            System.out.println(e);
-        }
-    }
-
-    /** does all the work of processing the request */
-    protected void processRequest() throws Exception {
-
-        headerVars = new DupHashtable();
-        urlVars = new DupHashtable();
-        postVars = new DupHashtable();
-        allVars = new DupHashtable();
-
-        String line;
-
-        // basic parsing of first req line
-        String reqLine = br.readLine();
-        printReq(reqLine);
-        String [] reqBits = reqLine.split("\\s+", 3);
-        reqType = reqBits[0];
-        String [] reqFileBits = reqBits[1].split("[?]", 2);
-        reqFile = reqFileBits[0];
-        
-        // check for URL variables
-        if (reqFileBits.length > 1) {
-            urlVars = parseVars(reqFileBits[1]);
-        }
-
-        // extract the 'request protocol', default to HTTP/1.0
-        try {
-            reqProto = reqBits[2];
-        } catch (Exception e) {
-            // workaround eepproxy bug
-            reqFile = "/";
-            reqProto = "HTTP/1.0";
-        }
-
-        // suck the headers
-        while (true) {
-            line = br.readLine();
-            //System.out.println("Got header line: "+line);
-            if (line.equals("")) {
-                break;
-            }
-            String [] lineBits = line.split(":\\s+", 2);
-            headerVars.put(lineBits[0], lineBits[1]);
-        }
-        //br.close();
-
-        // GET is simple, all the work is already done
-        if (reqType.equals("GET")) {
-            on_GET();
-        }
-
-        // POST is more involved - need to read POST data and
-        // break it up into fields
-        else if (reqType.equals("POST")) {
-            int postLen;
-            String postLenStr;
-            try {
-                reqContentType = headerVars.get("Content-Type", 0, "");
-
-                try {
-                    postLenStr = headerVars.get("Content-Length", 0);
-                } catch (Exception e) {
-                    // damn opera
-                    postLenStr = headerVars.get("Content-length", 0);
-                }
-
-                postLen = new Integer(postLenStr).intValue();
-                postData = new char[postLen];
-
-                //System.out.println("postLen="+postLen);
-                for (int i=0; i<postLen; i++) {
-                    int n = br.read();
-                    postData[i] = (char)n;
-                }
-                //input.read(postData);
-                postDataStr = new String(postData);
-                //System.out.println("post data: '"+postDataStr+"'");
-                
-                // detect RPC
-                if (reqContentType.equals("text/xml")
-                    && postDataStr.startsWith("<?xml")
-                )
-                {
-                    // yep, it's an rpc, fob off to handler
-                    ByteArrayInputStream in = new ByteArrayInputStream(postDataStr.getBytes());
-                    try {
-                        byte [] resp = server.xmlRpcServer.execute(in);
-                        setRawOutput(resp);
-                    } catch (Exception e) {
-                        e.printStackTrace();
-                        throw e;
-                    }
-                } else if (reqContentType.startsWith("multipart/form-data")) {
-                    // harder -parse as form
-                    postVars = parsMultipartForm(reqContentType, postDataStr);
-                    on_POST();
-                } else {
-                    // decode form vars
-                    postVars = parseVars(postDataStr);
-                    //System.out.println("postVars="+postVars);
-                    on_POST();
-                }
-            } catch (Exception e) {
-                e.printStackTrace();
-                setStatus("HTTP/1.0 400 Missing Content-Length header");
-                setRawOutput("Missing Content-Length header");
-            }
-        }
-
-        write(status+"\r\n");
-        write("Content-Type: "+contentType+"\r\n");
-        write("Server: "+server+"\r\n");
-
-        int contentLength;
-
-        if (rawContentBytes == null) {
-            // render out our html page
-            String rawPage = toString();
-            contentLength = rawPage.length();
-            write("Content-Length: "+rawPage.length()+"\r\n");
-            write("\r\n");
-            write(rawPage);
-
-        } else {
-            // sending raw output
-            write("Content-Length: "+rawContentBytes.length+"\r\n");
-            write("\r\n");
-            write(rawContentBytes);
-        }
-
-        output.flush();
-
-        try {
-            input.close();
-            output.close();
-            br.close();
-            closeSocket();
-        }
-        catch(Exception e) {}
-    }
-
-    /** helper method which, given a filename, returns a guess at
-     * a plausible mimetype for it
-     */
-    public String getContentType(String fileName) {
-        return Mimetypes.guessType(fileName);
-    }
-
-    /** a crude rfc-1867-subset form decoder, for allowing file
-     * uploads. For binary file upload, only supports
-     * application/octet-stream and application/x-macbinary at present
-     */
-    public DupHashtable parsMultipartForm(String reqContentType, String postDataStr) {
-        
-        DupHashtable flds = new DupHashtable();
-        
-        //System.out.println("contenttype='"+reqContentType+"'");
-        //System.out.println("raw postDataStr='"+postDataStr+"'");
-
-        // determine the 'boundary' string separating the form items
-        String boundary = reqContentType
-            .split("multipart/form-data;\\s*")
-            [1]
-            .split("boundary\\=")
-            [1];
-
-        // convert it to escaped string
-        boundary = "\\Q" + boundary + "\\E";
-
-        //System.out.println("boundary='"+boundary+"'");
-
-        // break up the raw post data into items
-        String [] postItems = postDataStr.split(boundary);
-
-        // try to extract a form variable from each item
-        for (int i=0; i<postItems.length; i++) {
-            
-            String item = postItems[i];
-            
-            // strip the newline at start
-            String [] items = item.split("^\\s+");
-            item = items[items.length-1];
-            
-            // strip the trailing '--'
-            try {
-                item = item.substring(0, item.length()-4);
-            } catch (StringIndexOutOfBoundsException e) {
-                item = item.substring(0, item.length()-2);
-            }
-            postItems[i] = item;
-
-            // break up item into headers+content
-            String [] bits = item.split("\\r\\n\\r\\n", 2);
-            String [] fldHdrs = bits[0].split("\\r\\n");
-            String fldContent;
-            try {
-                // get item content
-                fldContent = bits[1];
-            } catch (ArrayIndexOutOfBoundsException e) {
-                // no content
-                fldContent = "";
-            }
-
-            //System.out.println("-----------------------");
-
-            // go through the headers in search of 'name='
-            for (int j=0; j<fldHdrs.length; j++) {
-                //System.out.println("hdr: '"+fldHdrs[j]+"'");
-
-                // break up header into its parts
-                String [] hdrItems = fldHdrs[j].split(";\\s+");
-                
-                // go through each part in search of 'name='
-                for (int k=0; k<hdrItems.length; k++) {
-                    String hdrItem = hdrItems[k];
-
-                    if (hdrItem.startsWith("name=\"")) {
-                        // got a field name, add to our DupHashtable
-                        String varName = hdrItem.substring(6, hdrItem.length()-1);
-                        flds.put(varName, fldContent);
-                        allVars.put(varName, fldContent);
-                    }
-                }
-            }
-
-            //System.out.println("data("+fldContent.length()+"): '"+fldContent+"'");
-            
-            //System.out.println("postItem='"+postItems[i]+"'");
-            //byte [] b = fldContent.getBytes();
-            //for (int j=0; j<b.length; j++) {
-            //    System.out.print("" + b[j] + ", ");
-            //}
-            //System.out.println("");
-        }
-
-        return flds;
-
-// ----------------------------
-// raw sample of a posted form
-        
-/*
-Content-Type: multipart/form-data; boundary=---------------------------121990404611892642131748622646
-Content-Length: 1443
-
------------------------------121990404611892642131748622646
-Content-Disposition: form-data; name="cmd"
-
-put
------------------------------121990404611892642131748622646
-Content-Disposition: form-data; name="type"
-
-other
------------------------------121990404611892642131748622646
-Content-Disposition: form-data; name="title"
-
-
------------------------------121990404611892642131748622646
-Content-Disposition: form-data; name="path"
-
-
------------------------------121990404611892642131748622646
-Content-Disposition: form-data; name="mimetype"
-
-text/plain
------------------------------121990404611892642131748622646
-Content-Disposition: form-data; name="keywords"
-
-
------------------------------121990404611892642131748622646
-Content-Disposition: form-data; name="summary"
-
-
------------------------------121990404611892642131748622646
-Content-Disposition: form-data; name="data"; filename="tmpd.lst"
-Content-Type: application/octet-stream
-
-/tmp/d
-/tmp/d/d1
-/tmp/d/d1/f4
-/tmp/d/d1/f3
-/tmp/d/f2
-/tmp/d/f1
-
------------------------------121990404611892642131748622646
-Content-Disposition: form-data; name="privkey"
-
-
------------------------------121990404611892642131748622646
-Content-Disposition: form-data; name="submit"
-
-Insert it
------------------------------121990404611892642131748622646
-Content-Disposition: form-data; name="rawdata"
-
-
------------------------------121990404611892642131748622646--
-
-
-**/
-
-    }
-
-    public DupHashtable parseVars(String raw) {
-        DupHashtable h = new DupHashtable();
-        
-        URLDecoder u = new URLDecoder();
-        String [] items = raw.split("[&]");
-        String dec;
-        for (int i=0; i<items.length; i++) {
-            try {
-                dec = u.decode(items[i], "ISO-8859-1");
-                String [] items1 = dec.split("[=]",2);
-                //System.out.println("parseVars: "+items1[0]+"="+items1[1]);
-                h.put(items1[0], items1[1]);
-                allVars.put(items1[0], items1[1]);
-            } catch (Exception e) {
-                    e.printStackTrace();
-            }
-        }
-        
-        return h;
-    }
-
-    public void printReq(String r) {
-        System.out.println(r);
-    }
-
-    public Tag dumpVars() {
-
-        Tag t = new Tag("table "
-            +"width=90% cellspacing=0 cellpadding=4 border=1");
-        tag(t, "tr")
-            .nest("td colspan=2")
-                .nest("big")
-                    .nest("bold")
-                        .raw("Dump of session vars");
-        dumpVarsFor(t, headerVars, "HTTP header variables");
-        dumpVarsFor(t, urlVars, "URL variables");
-        dumpVarsFor(t, postVars, "POST variables");
-        return t;
-    }
-    
-    public void dumpVarsFor(Tag t, DupHashtable h, String heading) {
-        
-        Tag tr;
-        Tag td;
-        
-        //System.out.println("dumpVarsFor: map="+h);
-        
-        // add the html headers
-        tr = tag(t, "tr");
-        t.nest("tr")
-            .nest("td colspan=2 align=left")
-                .nest("b")
-                    .raw(heading);
-
-        //System.out.println("dumpVarsFor: heading="+heading);
-        Enumeration en = h.keys();
-        while (en.hasMoreElements()) {
-            String key = (String)en.nextElement();
-            Vector vals = h.get(key);
-            //System.out.println("dumpVarsFor: key="+key+" val="+vals);
-            for (int i=0; i<vals.size(); i++) {
-                tr = tag(t, "tr");
-                tr.nest("td").raw(i == 0 ? key : "&nbsp");
-                tr.nest("td").raw((String)vals.get(i));
-            }
-        }
-    }
-
-    /** creates an Tag object, and inserts it into
-     * a parent Tag
-     */
-    public Tag tag(Tag parent, String tagopen) {
-        
-        return new Tag(parent, tagopen);
-    }
-
-    /** creates an Tag object */
-    public Tag tag(String tagopen) {
-        return new Tag(tagopen);
-    }
-
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/http/MiniHttpRequestPage.java b/apps/q/java/src/net/i2p/aum/http/MiniHttpRequestPage.java
deleted file mode 100644
index b577311df8f3d369666d7980df912df9ed3dc1fd..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/http/MiniHttpRequestPage.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * MiniHttpRequestPage.java
- *
- * Created on April 13, 2005, 11:24 AM
- */
-
-package net.i2p.aum.http;
-
-/**
- *
- * @author  david
- */
-public class MiniHttpRequestPage extends MiniHttpRequestHandler {
-    
-    /** HtmlPage object into which we can write response */
-    protected HtmlPage page;
-
-    /** the 'head' portion of our response page */
-    protected Tag head;
-
-    /** the 'body' portion of our response page */
-    protected Tag body;
-
-    public MiniHttpRequestPage(MiniHttpServer server, Object socket) throws Exception {
-        super(server, socket, null);
-        this.page = new HtmlPage();
-        head = page.head;
-        body = page.body;
-    }
-
-    /** Creates a new instance of MiniHttpRequestPage */
-    public MiniHttpRequestPage(MiniHttpServer server, Object socket, Object arg) throws Exception {
-        super(server, socket, arg);
-        this.page = new HtmlPage();
-        head = page.head;
-        body = page.body;
-    }
-    
-    /**
-     * @param args the command line arguments
-     */
-    public static void main(String[] args) {
-
-    }
-    
-    public void on_GET() throws Exception {
-    }
-    
-    public void on_POST() throws Exception {
-    }
-    
-    public void on_RPC() throws Exception {
-    }
-    
-    /** adds a string of text, or an Tag object, to the
-     * body of the output page
-     */
-    public Tag add(String item) {
-        return body.add(item);
-    }
-    public Tag add(Tag item) {
-        return body.add(item);
-    }
-
-    /** sets up standard page's embedded stylesheet */
-    public void addCss() {
-
-        css("body", "font-family", "helvetica, arial, sans-serif");
-        css("body", "color", "green");
-        css("td", "vertical-align", "top");
-        css("code", "font-family: courier, monospace; font-weight: bold");
-        css(".border1, .pane1", "border-style: solid; border-width: 1px; border-color: blue");
-        css(".pane1", "background-color: #f0f0ff");
-        
-    }
-
-    /** adds a single CSS setting */
-    public HtmlPage css(String tag, String item, String val) {
-        return css(tag, item+":"+val);
-    }
-
-    /** adds a single CSS setting */
-    public HtmlPage css(String tag, String setting) {
-        page.css(tag, setting);
-        return page;
-    }
-
-    /** renders this page to full html document */
-    public String toString() {
-        return page.toString();
-    }
-
-}
diff --git a/apps/q/java/src/net/i2p/aum/http/MiniHttpServer.java b/apps/q/java/src/net/i2p/aum/http/MiniHttpServer.java
deleted file mode 100644
index 434b6a05c402ecf71e9363e63d0166b956fafcd4..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/http/MiniHttpServer.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * MiniHttpServer.java
- *
- * adapted and expanded from pont.net's httpServer.java
- * Created on April 8, 2005, 3:13 PM
- */
-
-package net.i2p.aum.http;
-
-//***************************************
-  // HTTP Server 
-  // fpont June 2000
-  // server implements HTTP GET method
-  //***************************************
-
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.net.ServerSocket;
-import java.net.Socket;
-
-import org.apache.xmlrpc.XmlRpcServer;
-
-public class MiniHttpServer extends Thread
-{
-    public Object serverSocket;
-    public static int defaultPort = 18000;
-    public int port;
-    public static Class defaultReqHandlerClass = MiniHttpRequestHandler.class;
-    public Class reqHandlerClass;
-    public Object reqHandlerArg = null;
-    public XmlRpcServer xmlRpcServer;
-
-    public MiniHttpServer() {
-        this(defaultReqHandlerClass, defaultPort);
-    }
-
-    public MiniHttpServer(Class reqHandlerClass) {
-        this(reqHandlerClass, defaultPort);
-    }
-
-    public MiniHttpServer(Class reqHandlerClass, Object hdlrArg) {
-        this(reqHandlerClass, defaultPort, hdlrArg);
-    }
-
-    public MiniHttpServer(int port) {
-        this(defaultReqHandlerClass, port);
-    }
-
-    public MiniHttpServer(Class reqHandlerClass, int port) {
-        this(reqHandlerClass, port, null);
-    }
-    
-    public MiniHttpServer(Class reqHandlerClass, int port, Object hdlrArg) {
-        super();
-        this.port = port;
-        this.reqHandlerClass = reqHandlerClass;
-        this.reqHandlerArg = hdlrArg;
-        xmlRpcServer = new XmlRpcServer();
-    }
-
-    // override these following methods if you're using sockets other than ServerSocket
-
-    /**
-     * Gets a server socket object, and assigns it to property 'serverSocket'.
-     * You should override this if you're using non-socket objects
-     */
-    public void getServerSocket() throws IOException {
-
-        serverSocket = new ServerSocket(port);
-
-        try {
-            System.out.println("httpServer running on port "
-                + ((ServerSocket)serverSocket).getLocalPort());
-        } catch (Exception e) {
-            
-        }
-    }
-
-    /**
-     * Listens on our 'serverSocket' object for an incoming connection,
-     * and returns a connected socket object. You should override this
-     * if you're using non-standard socket objects
-     */
-    public Object acceptConnection() throws IOException {
-
-        Socket sock = ((ServerSocket)serverSocket).accept();
-        //System.out.println("New connection accepted" +
-        //                    sock.getInetAddress() +
-        //                    ":" + sock.getPort());
-        return sock;
-    }
-
-    /**
-     * Invokes constructor on the 'reqHandlerClass' with which 
-     * this server was constructed.
-     */
-    public MiniHttpRequestHandler getRequestHandler(Object sock) throws Exception {
-        //return new MiniHttpRequestHandlerImpl(sock);
-        Class [] consArgTypes = {MiniHttpServer.class, Object.class, Object.class};
-        Object [] consArgs = {this, sock, reqHandlerArg};
-        Constructor cons = reqHandlerClass.getConstructor(consArgTypes);
-
-        try {
-            MiniHttpRequestHandler reqHdlr = (MiniHttpRequestHandler)(cons.newInstance(consArgs));
-            return reqHdlr;
-        } catch (InvocationTargetException e) {
-            Throwable e1 = e.getTargetException();
-            e1.printStackTrace();
-            throw e;
-        }
-    }
-
-    /**
-     * Adds an xmlrpc handler.
-     * Ideally, the handler object should have been constructed
-     * with 'this' as an argument.
-     */
-    public void addXmlRpcHandler(String name, Object handler) {
-        xmlRpcServer.addHandler(name, handler);
-    }
-
-    /**
-     * starts up the server and enters an infinite loop,
-     * forever servicing requests
-     */
-    public void run() {
-
-        Object server_socket;
-	
-        try {
-            String clsName = getClass().getName();
-	    // impl-dependent - procure a server socket
-            System.out.println(clsName + ": calling getServerSocket...");
-            getServerSocket();
-            System.out.println(clsName + ": got server socket");
-	    
-            // server infinite loop
-            while(true) {
-
-                System.out.println(clsName + ": awaiting inbound connection...");
-
-                // impl-dependent - accept incoming connection
-                Object sock = acceptConnection();
-		
-                // Construct handler to process the HTTP request message.
-                try {
-                    MiniHttpRequestHandler request = getRequestHandler(sock);
-
-                    // Create a new thread to process the request.
-                    Thread thread = new Thread(request);
-		    
-                    // Start the thread.
-                    thread.start();
-                }
-                catch(Exception e) {
-                    //System.out.println(e);
-                    e.printStackTrace();
-                }
-            }
-        }
-	
-        catch (IOException e) {
-            System.out.println(e);
-        }
-    }
-
-    public static void main(String [] args) {
-
-        /** create a simple request handler */
-        class DemoHandler extends MiniHttpRequestPage {
-            public DemoHandler(MiniHttpServer server, Object sock, Object arg)
-                throws Exception
-            {
-                super(server, sock, arg);
-            }
-
-            public void on_GET() {
-                on_hit();
-            }
-            
-            public void on_POST() {
-                on_hit();
-            }
-
-            public void on_hit() {
-                setContentType("text/html");
-                page.head.nest("title").raw("DemoHandler");
-                page.body
-                    .nest("h3")
-                        .raw("DemoHandler")
-                        .end
-                    .hr()
-                    .raw("reqFile="+reqFile)
-                    .br()
-                    .raw("reqType="+reqType)
-                    .hr()
-                    .add(dumpVars())
-                ;
-            }
-        }
-
-        MiniHttpServer server;
-        int port = defaultPort;
-        if (args.length > 0) {
-            try {
-                port = new Integer(args[0]).intValue();
-            } catch (NumberFormatException e) {
-                System.out.println("Invalid port: "+args[0]);
-                System.exit(1);
-            }
-
-            server = new MiniHttpServer(DemoHandler.class, port);
-        }
-        else {
-            server = new MiniHttpServer(DemoHandler.class);
-        }
-
-        MiniDemoXmlRpcHandler hdlr = new MiniDemoXmlRpcHandler(server);
-        server.addXmlRpcHandler("foo", hdlr);
-        
-        server.run();
-    }
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/http/Tag.java b/apps/q/java/src/net/i2p/aum/http/Tag.java
deleted file mode 100644
index 6370d1339a36fce7ce68a7dabbf5630024e6027d..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/http/Tag.java
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * HtmlTag.java
- *
- * Created on April 8, 2005, 8:22 PM
- */
-
-package net.i2p.aum.http;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Enumeration;
-import java.util.Vector;
-
-/**
- * Base class for building up quick-n-dirty HTML by code alone;
- * Breaks every design rule by enmeshing content, presentation and logic together
- * into java statements.
- */
-public class Tag {
-    
-    static Vector nlOnOpen = new Vector();
-    static {
-        nlOnOpen.addElement("html");
-        nlOnOpen.addElement("html");
-        nlOnOpen.addElement("head");
-        nlOnOpen.addElement("body");
-        nlOnOpen.addElement("frameset");
-        nlOnOpen.addElement("frame");
-        nlOnOpen.addElement("script");
-        nlOnOpen.addElement("blockquote");
-        nlOnOpen.addElement("div");
-        nlOnOpen.addElement("hr");
-        nlOnOpen.addElement("ul");
-        nlOnOpen.addElement("ol");
-        nlOnOpen.addElement("table");
-        nlOnOpen.addElement("caption");
-        nlOnOpen.addElement("col");
-        nlOnOpen.addElement("thead");
-        nlOnOpen.addElement("tfoot");
-        nlOnOpen.addElement("tbody");
-        nlOnOpen.addElement("tr");
-        nlOnOpen.addElement("form");
-        nlOnOpen.addElement("applet");
-        nlOnOpen.addElement("br");
-        nlOnOpen.addElement("style");
-        };
-
-    static Vector nlOnClose = new Vector();
-    static {
-        nlOnClose.addElement("h1");
-        nlOnClose.addElement("h2");
-        nlOnClose.addElement("h3");
-        nlOnClose.addElement("h4");
-        nlOnClose.addElement("h5");
-        nlOnClose.addElement("h6");
-        nlOnClose.addElement("p");
-        nlOnClose.addElement("pre");
-        nlOnClose.addElement("li");
-        nlOnClose.addElement("td");
-        nlOnClose.addElement("th");
-        nlOnClose.addElement("button");
-        nlOnClose.addElement("input");
-        nlOnClose.addElement("label");
-        nlOnClose.addElement("select");
-        nlOnClose.addElement("option");
-        nlOnClose.addElement("textarea");
-        nlOnClose.addElement("font");
-        nlOnClose.addElement("iframe");
-        nlOnClose.addElement("img");
-        nlOnClose.addElement("br");
-    }
-
-    String open;
-    String close;
-    Vector attribs;
-    Vector styles;
-    Vector content;
-    boolean breakBefore, breakAfter;
-    public Tag parent = null;
-    public Tag end = null;
-
-    // -----------------------------------------------------
-    // CONSTRUCTORS
-    // -----------------------------------------------------
-
-    /** Creates a new empty container tag */
-    public Tag() {
-        this((String)null);
-    }
-
-    /** Creates a new empty container tag, embedded in a parent tag */
-    public Tag(Tag parent) {
-        this(parent, null);
-    }
-
-    /**
-     * Creates a new HtmlTag instance, adds to a parent
-     */
-    public Tag(Tag parent, String opentag) {
-        this(opentag);
-        parent.add(this);
-        this.end = this.parent = parent;
-    }
-
-    /** Creates a new instance of HtmlTag */
-    public Tag(String opentag) {
-
-        content = new Vector();
-        attribs = new Vector();
-        styles = new Vector();
-
-        if (opentag == null) {
-            return;
-        }
-
-        String [] tagBits = opentag.split("\\s+", 2);
-        open = tagBits[0];
-
-        if (open.endsWith("/")) {
-            open = open.substring(0, open.length()-1);
-            close = "";
-        }
-        else {
-            close = "</"+open+">";
-        }
-
-        if (tagBits.length > 1) {
-            attribs.addElement(tagBits[1]);
-        }
-
-        breakBefore = nlOnOpen.contains(open);
-        breakAfter = breakBefore || nlOnClose.contains(open);
-    }
-
-    // -----------------------------------------------------
-    // METHODS FOR ADDING SPECIFIC HTML TAGS
-    // -----------------------------------------------------
-    
-    /** insert a &lt;br&gt; on the fly */
-    public Tag br() {
-        return add("br/");
-    }
-
-    /** insert a &lt;hr&gt; on the fly */
-    public Tag hr() {
-        return add("hr/");
-    }
-
-    public Tag center() {
-        return nest("center");
-    }
-
-    public Tag center(String attr) {
-        return nest("center "+attr);
-    }
-
-    public Tag big() {
-        return nest("big");
-    }
-
-    public Tag big(String attr) {
-        return nest("big "+attr);
-    }
-
-    public Tag small() {
-        return nest("small");
-    }
-
-    public Tag small(String attr) {
-        return nest("small "+attr);
-    }
-
-    public Tag i() {
-        return nest("i");
-    }
-
-    public Tag i(String attr) {
-        return nest("i "+attr);
-    }
-
-    public Tag strong() {
-        return nest("strong");
-    }
-
-    public Tag strong(String attr) {
-        return nest("big "+attr);
-    }
-
-    public Tag table() {
-        return nest("table");
-    }
-
-    public Tag table(String attr) {
-        return nest("table "+attr);
-    }
-
-    public Tag tr() {
-        return nest("tr");
-    }
-
-    public Tag tr(String attr) {
-        return nest("tr "+attr);
-    }
-
-    public Tag td() {
-        return nest("td");
-    }
-
-    public Tag td(String attr) {
-        return nest("td "+attr);
-    }
-
-    public Tag form() {
-        return nest("form");
-    }
-
-    public Tag form(String attr) {
-        return nest("form "+attr);
-    }
-
-    // -----------------------------------------------------
-    // METHODS FOR ADDING GENERAL CONTENT
-    // -----------------------------------------------------
-
-    /** create a new tag, embed it into this one, return this tag */
-    public Tag add(String s) {
-        Tag t = new Tag(s);
-        content.addElement(t);
-        return this;
-    }
-
-    /** add a tag to this one, returning this tag */
-    public Tag add(Tag t) {
-        content.addElement(t);
-        return this;
-    }
-
-    /** create a new tag, nest it into this one, return the new tag */
-    public Tag nest(String opentag) {
-        Tag t = new Tag(this, opentag);
-        t.parent = this;
-        return t;
-    }
-    public Tag nest() {
-        Tag t = new Tag(this);
-        t.parent = this;
-        return t;
-    }
-    
-    /** insert object into this tag, return this tag */
-    public Tag raw(Object o) {
-        content.addElement(o);
-        return this;
-    }
-
-    /** set an attribute of this tag, return this tag */
-    public Tag set(String name, String val) {
-        return set(name + "=\"" + val + "\"");
-    }
-
-    /** set an attribute of this tag, return this tag */
-    public Tag set(String setting) {
-        attribs.addElement(setting);
-        return this;
-    }
-
-    public Tag style(String name, String val) {
-        return style(name+":"+val);
-    }
-    
-    public Tag style(String setting) {
-        styles.addElement(setting);
-        return this;
-    }
-
-    // -----------------------------------------------------
-    // METHODS FOR RENDERING
-    // -----------------------------------------------------
-
-    public void render(OutputStream out) throws IOException {
-        
-        //System.out.print("{render:"+open+"}");
-        //System.out.flush();
-
-        if (open != null) {
-            out.write("<".getBytes());
-            out.write(open.getBytes());
-
-            // add in attributes, if any
-            for (int i=0; i<attribs.size(); i++) {
-                String attr = null;
-                try {
-                    attr = (String)attribs.get(i);
-                    //buf.append(" "+attr[0]+"="+attr[1]);
-                    out.write((" "+attr).getBytes());
-                } catch (ClassCastException e) {
-                    e.printStackTrace();
-                    //System.out.println("attr='"+attribs.get(i)+"'");
-                    System.out.println("attribs='"+attribs+"'");
-                    //System.out.println("content='"+content+"'");
-                }
-            }
-
-            // add in styles, if any
-            if (styles.size() > 0) {
-                out.write((" style=\"").getBytes());
-                Enumeration elems = styles.elements();
-                while (elems.hasMoreElements()) {
-                    String s = (String)elems.nextElement()+";";
-                    out.write(s.getBytes());
-                }
-                out.write("\"".getBytes());
-            }
-            
-            if (close.equals("")) {
-                out.write("/".getBytes());
-            }
-            out.write(">".getBytes());
-
-            if (breakBefore) {
-                out.write("\n".getBytes());
-            }
-        }
-
-        for (int i=0; i < content.size(); i++) {
-            Object item = content.get(i);
-            if (item.getClass().isAssignableFrom(Tag.class)) {
-                ((Tag)item).render(out);
-            } else {
-                out.write(item.toString().getBytes());
-            }
-        }
-
-        if (open != null) {
-            out.write(close.getBytes());
-            //buf.append(close);
-
-            if (breakAfter) {
-                out.write("\n".getBytes());
-            }
-        }
-    }
-
-    public String render() {
-        ByteArrayOutputStream s = new ByteArrayOutputStream();
-        try {
-            render(s);
-        } catch (Exception e) {
-            e.printStackTrace();
-            return null;
-        }
-        return s.toString();
-    }
-
-    public String toString() {
-        return render();
-    }
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/q/Favicon.java b/apps/q/java/src/net/i2p/aum/q/Favicon.java
deleted file mode 100644
index f5f6ab6c91c6b7cc72e4d59fcda4aec81647b143..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/Favicon.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package net.i2p.aum.q;
-public class Favicon {
-    public static byte [] image = {
-        0, 0, 1, 0, 1, 0, 16, 16, 0, 0, 1, 0, 24, 0, 104, 3, 
-        0, 0, 22, 0, 0, 0, 40, 0, 0, 0, 16, 0, 0, 0, 32, 0, 
-        0, 0, 1, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 0, 
-        0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 
-        19, 19, -127, -127, -127, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, -120, 
-        -120, -120, -49, -49, -49, 116, 116, 116, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 90, 90, -80, -80, -80, 
-        -18, -18, -18, -55, -55, -55, -122, -122, -122, 68, 68, 68, 107, 107, 107, -62, 
-        -62, -62, -20, -20, -20, -59, -59, -59, 4, 4, 4, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, -109, -109, -109, -30, -30, -30, -8, -8, -8, 
-        -25, -25, -25, -2, -2, -2, -28, -28, -28, -49, -49, -49, -2, -2, -2, -14, 
-        -14, -14, -36, -36, -36, 33, 33, 33, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 72, 72, 72, -1, -1, -1, -1, -1, -1, -28, -28, -28, 
-        -34, -34, -34, 118, 118, 118, -124, -124, -124, -1, -1, -1, -1, -1, -1, -6, 
-        -6, -6, 68, 68, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, -98, -98, -98, -1, -1, -1, -38, -38, -38, -80, -80, -80, 
-        13, 13, 13, 0, 0, 0, 100, 100, 100, -11, -11, -11, -9, -9, -9, -3, 
-        -3, -3, -90, -90, -90, 10, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, -49, -49, -49, -4, -4, -4, -57, -57, -57, 63, 63, 63, 
-        0, 0, 0, 26, 26, 26, -74, -74, -74, -56, -56, -56, -35, -35, -35, -29, 
-        -29, -29, -13, -13, -13, 104, 104, 104, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, -28, -28, -28, -46, -46, -46, -22, -22, -22, 2, 2, 2, 
-        0, 0, 0, 2, 2, 2, 41, 41, 41, 108, 108, 108, 37, 37, 37, -32, 
-        -32, -32, -29, -29, -29, -60, -60, -60, 1, 1, 1, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, -60, -60, -60, -60, -60, -60, -44, -44, -44, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, -56, 
-        -56, -56, -49, -49, -49, -43, -43, -43, 24, 24, 24, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, -117, -117, -117, -70, -70, -70, -48, -48, -48, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 50, 50, -93, 
-        -93, -93, -12, -12, -12, -47, -47, -47, 32, 32, 32, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 54, 54, 54, -42, -42, -42, -79, -79, -79, 28, 28, 28, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 110, 110, 110, -70, 
-        -70, -70, -4, -4, -4, -64, -64, -64, 3, 3, 3, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 121, 121, 121, -51, -51, -51, -87, -87, -87, 
-        10, 10, 10, 0, 0, 0, 37, 37, 37, -119, -119, -119, -106, -106, -106, -20, 
-        -20, -20, -33, -33, -33, 95, 95, 95, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 1, 1, 1, -124, -124, -124, -23, -23, -23, 
-        -33, -33, -33, -107, -107, -107, -75, -75, -75, -68, -68, -68, -15, -15, -15, -16, 
-        -16, -16, -111, -111, -111, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 
-        -97, -97, -97, -26, -26, -26, -29, -29, -29, -31, -31, -31, -61, -61, -61, 121, 
-        121, 121, 11, 11, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-        };
-}
diff --git a/apps/q/java/src/net/i2p/aum/q/QClientAPI.java b/apps/q/java/src/net/i2p/aum/q/QClientAPI.java
deleted file mode 100644
index a431283df76d209ae47624349d77a8c718ee37c4..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QClientAPI.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * QClientAPI.java
- *
- * Created on March 31, 2005, 5:19 PM
- */
-
-package net.i2p.aum.q;
-
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.util.Hashtable;
-import java.util.Vector;
-
-import org.apache.xmlrpc.XmlRpcClient;
-import org.apache.xmlrpc.XmlRpcException;
-
-/**
- * <p>The official Java API for client applications wishing to access the Q
- * network</p>
- * <p>This API is just a thin wrapper that hides the XMLRPC details, and exposes
-   a simple set of methods.</p>
- * <p>Note to app developers - I'm only implementing this API in Java
- * and Python at present. If you've got some time and knowledge of other
- * languages and their available XML-RPC client libs, we'd really appreciate
- * it if you can port this API into other languages - such as Perl, C++,
- * Ruby, OCaml, C#, etc. You can take this API implementation as the reference
- * code for porting to your own language.</p>
- */
-
-public class QClientAPI {
-
-    XmlRpcClient node;
-
-    /**
-     * Creates a new instance of QClientAPI talking on given xmlrpc port
-     */
-    public QClientAPI(int port) throws MalformedURLException {
-        node = new XmlRpcClient("http://127.0.0.1:"+port);
-    }
-
-    /**
-     * Creates a new instance of QClientAPI talking on default xmlrpc port
-     */
-    public QClientAPI() throws MalformedURLException {
-        node = new XmlRpcClient("http://127.0.0.1:"+QClientNode.defaultXmlRpcServerPort);
-    }
-
-    /**
-     * Pings a Q client node, gets back a bunch of useful stats
-     */
-    public Hashtable ping() throws XmlRpcException, IOException {
-        return (Hashtable)node.execute("i2p.q.ping", new Vector());
-    }
-
-    /**
-     * Retrieves an update of content catalog
-     * @param since a unixtime in seconds. The content list returned will
-     * be a differential update since this time.
-     */
-    public Hashtable getUpdate(int since)
-        throws XmlRpcException, IOException
-    {
-        Vector args = new Vector();
-        args.addElement(new Integer(since));
-        args.addElement(new Integer(1));
-        args.addElement(new Integer(1));
-        return (Hashtable)node.execute("i2p.q.getUpdate", args);
-    }
-
-    /**
-     * Retrieves an item of content from the network, given its key
-     * @param key the key to retrieve
-     */
-    public Hashtable getItem(String key) throws XmlRpcException, IOException {
-        Vector args = new Vector();
-        args.addElement(key);
-        return (Hashtable)node.execute("i2p.q.getItem", args);
-    }
-
-    /** 
-     * Inserts a single item of data, without metadata. A default metadata set
-     * will be generated.
-     * @param data a byte[] of data to insert
-     * @return a Hashtable containing results, including:
-     * <ul>
-     *  <li>result - either "ok" or "error"</li>
-     *  <li>error - (only if result != "ok") - terse error label</li>
-     *  <li>key - the key under which this item has been inserted</li>
-     * </ul>
-     */
-    public Hashtable putItem(byte [] data) throws XmlRpcException, IOException {
-        Vector args = new Vector();
-        args.addElement(data);
-        return (Hashtable)node.execute("i2p.q.putItem", args);
-    }
-
-    /** 
-     * Inserts a single item of data, with metadata
-     * @param metadata a Hashtable of metadata to insert
-     * @param data a byte[] of data to insert
-     * @return a Hashtable containing results, including:
-     * <ul>
-     *  <li>result - either "ok" or "error"</li>
-     *  <li>error - (only if result != "ok") - terse error label</li>
-     *  <li>key - the key under which this item has been inserted</li>
-     * </ul>
-     */
-    public Hashtable putItem(Hashtable metadata, byte [] data)
-        throws XmlRpcException, IOException
-    {
-        Vector args = new Vector();
-        args.addElement(metadata);
-        args.addElement(data);
-        return (Hashtable)node.execute("i2p.q.putItem", args);
-    }
-
-    /**
-     * Generates a new keypair for inserting signed-space items
-     * @return a struct with the keys:
-     * <ul>
-     *  <li>status - "ok"</li>
-     *  <li>publicKey - base64-encoded signed space public key</li>
-     *  <li>privateKey - base64-encoded signed space private key</li>
-     * </ul>
-     * When inserting an item using the privateKey, the resulting uri
-     * will be <code>Q:publicKey/path</code>
-     */
-    public Hashtable newKeys() throws XmlRpcException, IOException
-    {
-        Vector args = new Vector();
-        return (Hashtable)node.execute("i2p.q.newKeys", args);
-    }
-
-    
-    /**
-     * Adds a new noderef to node
-     * @param dest - the base64 i2p destination for the remote peer
-     * @return a Hashtable containing results, including:
-     * <ul>
-     *  <li>result - either "ok" or "error"</li>
-     *  <li>error - (only if result != "ok") - terse error label</li>
-     * </ul>
-     */
-    public Hashtable hello(String dest) throws XmlRpcException, IOException {
-        Vector args = new Vector();
-        args.addElement(dest);
-        return (Hashtable)node.execute("i2p.q.hello", args);
-    }
-
-    /**
-     * Shuts down a running node
-     * If the shutdown succeeds, then this call will fail with an exception. But
-     * if the call succeeds, then the shutdown has failed (sorry if this is a tad
-     * counter-intuitive).
-     * @param privKey - the base64 i2p private key for this node.
-     * @return a Hashtable containing results, including:
-     * <ul>
-     *  <li>result - "error"</li>
-     *  <li>error - terse error label</li>
-     * </ul>
-     */
-    public Hashtable shutdown(String privKey) throws XmlRpcException, IOException {
-        Vector args = new Vector();
-        args.addElement(privKey);
-        return (Hashtable)node.execute("i2p.q.shutdown", args);
-    }
-
-    /**
-     * Search the node for catalog entries matching a set of criteria
-     * @param criteria a Hashtable of metadata criteria to match, and whose
-     * values are regular expressions
-     * @return a Hashtable containing results, including:
-     * <ul>
-     *  <li>result - "ok" or "error"</li>
-     *  <li>error - if result != "ok", a terse error label</li>
-     *  <li>items - a Vector of items found which match the given search
-     *     criteria. If no available matching items were found, this vector
-     *     will come back empty.
-     * </ul>
-     */
-    public Hashtable search(Hashtable criteria) throws XmlRpcException, IOException {
-        Vector args = new Vector();
-        args.addElement(criteria);
-        return (Hashtable)node.execute("i2p.q.search", args);
-    }
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/q/QClientNode.java b/apps/q/java/src/net/i2p/aum/q/QClientNode.java
deleted file mode 100644
index 4cd7b37c3594dd2cd392f32c1861e8bd4f1a4ca2..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QClientNode.java
+++ /dev/null
@@ -1,608 +0,0 @@
-/*
- * QClient.java
- *
- * Created on 20 March 2005, 23:22
- */
-
-package net.i2p.aum.q;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.Properties;
-import java.util.Vector;
-
-import net.i2p.I2PException;
-import net.i2p.aum.Mimetypes;
-import net.i2p.aum.http.I2PHttpServer;
-import net.i2p.aum.http.MiniHttpServer;
-import net.i2p.data.DataFormatException;
-
-/**
- * Implements Q client nodes.
- */
-
-public class QClientNode extends QNode {
-    
-    static String defaultStoreDir = ".quartermaster_client";
-    I2PHttpServer webServer;
-    MiniHttpServer webServerTcp;
-    Properties httpProps;
-
-    public String nodeType = "Client";
-
-    // -------------------------------------------------------
-    // CONSTRUCTORS
-    // -------------------------------------------------------
-
-    /**
-     * Creates a new instance of QClient, using default
-     * datastore location
-     * @throws IOException, DataFormatException, I2PException
-     */
-    public QClientNode() throws IOException, DataFormatException, I2PException
-    {
-        super(System.getProperties().getProperty("user.home") + sep + defaultStoreDir);
-        log.debug("TEST CLIENT DEBUG MSG1");
-    }
-
-    /**
-     * Creates a new instance of QClient, using specified
-     * datastore location
-     * @param path of node's datastore directory
-     * @throws IOException, DataFormatException, I2PException
-     */
-    public QClientNode(String dataDir) throws IOException, DataFormatException, I2PException
-    {
-        super(dataDir);
-        
-        log.error("TEST CLIENT DEBUG MSG");
-    }
-
-    // -------------------------------------------------------
-    // METHODS - XML-RPC PRIMITIVE OVERRIDES
-    // -------------------------------------------------------
-
-    /**
-     * hello cmds to client nodes are illegal!
-     */
-    /**
-     public Hashtable localHello(String destBase64)
-     {
-        Hashtable h = new Hashtable();
-        h.put("status", "error");
-        h.put("error", "unimplemented");
-        return h;
-    }
-    **/
-
-    /** perform client-specific setup */
-    public void setup()
-    {
-        updateCatalogFromPeers = 1;
-        isClient = true;
-
-        // allow a port change for xmlrpc client app conns
-        String xmlPortStr = System.getProperty("q.xmlrpc.tcp.port");
-        if (xmlPortStr != null) {
-            xmlRpcServerPort = new Integer(xmlPortStr).intValue();
-            conf.setIntProperty("xmlRpcServerPort", xmlRpcServerPort);
-        }
-
-        // ditto for listening host
-        String xmlHostStr = System.getProperty("q.xmlrpc.tcp.host");
-        if (xmlHostStr != null) {
-            xmlRpcServerHost = xmlHostStr;
-            conf.setProperty("xmlRpcServerHost", xmlRpcServerHost);
-        }
-
-        // ---------------------------------------------------
-        // now fire up the HTTP interface
-        // listening only within I2P on client node's dest
-        
-        // set up a properties object for short local tunnel
-        httpProps = new Properties();
-        httpProps.setProperty("inbound.length", "0");
-        httpProps.setProperty("outbound.length", "0");
-        httpProps.setProperty("inbound.lengthVariance", "0");
-        httpProps.setProperty("outbound.lengthVariance", "0");
-        Properties sysProps = System.getProperties();
-        String i2cpHost = sysProps.getProperty("i2cp.tcp.host", "127.0.0.1");
-        String i2cpPort = sysProps.getProperty("i2cp.tcp.port", "7654");
-        httpProps.setProperty("i2cp.tcp.host", i2cpHost);
-        httpProps.setProperty("i2cp.tcp.port", i2cpPort);
-    }
-
-    public void run() {
-    
-        // then do all the parent stuff
-        super.run();
-    }
-
-    /**
-     * <p>Sets up and launches an http server for servicing requests
-     * to this node.</p>
-     * <p>For server nodes, the xml-rpc server listens within I2P on the
-     * node's destination.</p>
-     * <p>For client nodes, the xml-rpc server listens on a local TCP
-     * port (according to attributes xmlRpcServerHost and xmlRpcServerPort)</p>
-     */
-    public void startExternalInterfaces(QServerMethods methods) throws Exception
-    {
-        System.out.println("Creating http interface...");
-        try {
-            // create tcp http server for xmlrpc and browser access
-            webServerTcp = new MiniHttpServer(QClientWebInterface.class, xmlRpcServerPort, this);
-            webServerTcp.addXmlRpcHandler(baseXmlRpcServiceName, methods);
-            System.out.println("started in-i2p http/xmlrpc server listening on port:" + xmlRpcServerPort);
-            webServerTcp.start();
-
-            // create in-i2p http server for xmlrpc and browser access
-            webServer
-                = new I2PHttpServer(privKey,
-                                    QClientWebInterface.class,
-                                    this,
-                                    httpProps
-                                    );
-            webServer.addXmlRpcHandler(baseXmlRpcServiceName, methods);
-            webServer.start();
-            System.out.println("Started in-i2p http/xmlrpc server listening on dest:");
-            String dest = privKey.getDestination().toBase64();
-            System.out.println(dest);
-            
-
-        System.out.println("web interfaces created");
-
-        } catch (Exception e) {
-            e.printStackTrace();
-            System.out.println("Failed to create client web interfaces");
-            System.exit(1);
-        }
-
-/**        
-        WebServer serv = new WebServer(xmlRpcServerPort);
-        // if host is non-null, add as a listen host
-        if (xmlRpcServerHost.length() > 0) {
-            serv.setParanoid(true);
-            serv.acceptClient(xmlRpcServerHost);
-        }
-        serv.addHandler(baseXmlRpcServiceName, methods);
-        serv.start();
-        log.info("Client XML-RPC server listening on port "+xmlRpcServerPort+" as service"+baseXmlRpcServiceName);
-**/
-
-    }
-
-    // -----------------------------------------------------
-    // client-specific customisations of xmlRpc methods
-    // -----------------------------------------------------
-
-    /**
-     * Insert an item of content, with metadata. Then (since this is the client's
-     * override) schedules a job to insert this item to a selection of remote peers.
-     * @param metadata Hashtable of item's metadata
-     * @param data raw data to insert
-     */
-    public Hashtable putItem(Hashtable metadata, byte [] data) throws QException
-    {
-        Hashtable resp = new Hashtable();
-        QDataItem item;
-        String uri;
-    
-        // do the local insert first
-        try {
-            item = new QDataItem(metadata, data);
-            item.processAndValidate(true);
-            localPutItem(item);
-            uri = (String)item.get("uri");
-
-        } catch (QException e) {
-            resp.put("status", "error");
-            resp.put("error", "qexception");
-            resp.put("summary", e.getLocalizedMessage());
-            return resp;
-        }
-
-        // now schedule remote insertion
-        schedulePeerUploadJob(item);
-
-        // and return success, rest will happen automatically in background
-        resp.put("status", "ok");
-        resp.put("uri", uri);
-        return resp;
-    }
-
-    /**
-     * Search datastore and catalog for a given item of content
-     * @param criteria Hashtable of criteria to match in metadata
-     */
-    public Hashtable search(Hashtable criteria)
-    {
-        Hashtable result = new Hashtable();
-        Vector matchingItems = new Vector();
-        Iterator items;
-        Hashtable item;
-        Hashtable foundUris = new Hashtable();
-        String uri;
-        
-        // get an iterator for all catalog items
-        try {
-            // test all local content
-            items = contentIdx.getItemsSince(0);
-            while (items.hasNext()) {
-                String uriHash = (String)items.next();
-                item = getLocalMetadataForHash(uriHash);
-                uri = (String)item.get("uri");
-                //System.out.println("search: testing "+metadata+" against "+criteria);
-                if (metadataMatchesCriteria(item, criteria)) {
-                    matchingItems.addElement(item);
-                    foundUris.put(uri, item);
-                }
-            }
-
-            // now test remote catalog
-            items = catalogIdx.getItemsSince(0);
-            while (items.hasNext()) {
-                String uriHash = (String)items.next();
-                item = getLocalCatalogMetadataForHash(uriHash);
-                uri = (String)item.get("uri");
-                //System.out.println("search: testing "+metadata+" against "+criteria);
-                if (metadataMatchesCriteria(item, criteria)) {
-                    if (!foundUris.containsKey("uri")) {
-                        matchingItems.addElement(item);
-                    }
-                }
-            }
-            
-        } catch (Exception e) {
-            e.printStackTrace();
-            result.put("status", "error");
-            result.put("error", e.getMessage());
-            return result;
-        }
-
-        result.put("status", "ok");
-        result.put("items", matchingItems);
-        return result;
-        
-    }
-
-
-    /**
-     * retrieves a peers/catalog update - executes on base class, then
-     * adds in our catalog entries
-     */
-    public Hashtable getUpdate(int since, int includePeers, int includeCatalog)
-    {
-        Hashtable h = localGetUpdate(since, includePeers, includeCatalog);
-
-        if (includeCatalog != 0) {
-
-            // must extend v with remote catalog entries
-            Vector vCat = (Vector)(h.get("items"));
-            Iterator items;
-
-            // get an iterator for all new catalog items since given unixtime
-            try {
-                items = catalogIdx.getItemsSince(since);
-
-                // pick through the iterator, and fetch metadata for each item
-                while (items.hasNext()) {
-                    String key = (String)(items.next());
-                    Hashtable pf = getLocalCatalogMetadata(key);
-                    log.error("getUpdate(client): key="+key+", pf="+pf);
-                    System.out.println("getUpdate(client): key="+key+", pf="+pf);
-                    if (pf != null) {
-                        // clone this metadata, add in the key
-                        Hashtable pf1 = (Hashtable)pf.clone();
-                        pf1.put("key", key);
-                        vCat.addElement(pf1);
-                    }
-                }
-
-
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-        }
-
-        return h;
-    }
-
-    /**
-     * <p>Retrieve an item of content.</p>
-     * <p>This client override tries the local datastore first, then
-     * attempts to get the data from remote servers believed to have the data</p>
-     */
-    public Hashtable getItem(String uri) throws IOException, QException
-    {
-        Hashtable res;
-
-        log.info("getItem: uri='"+uri+"'");
-
-        if (localHasItem(uri)) {
-
-            class Fred {
-            }
-            
-            Fred xxx = new Fred();
-            
-            // got it locally, send it back
-            return localGetItem(uri);
-        }
-        
-        // ain't got it locally - try remote sources in turn till we
-        // either get it or fail
-        Vector sources = getItemLocation(uri);
-
-        // send back an error if not in local catalog
-        if (sources == null || sources.size() == 0) {
-            Hashtable dnf = new Hashtable();
-            dnf.put("status", "error");
-            dnf.put("error", "notfound");
-            dnf.put("comment", "uri not known locally or remotely");
-            return dnf;
-        }
-
-        // ok, got at least one remote source, go through them till
-        // we get data that checks out
-        int i;
-        int npeers = sources.size();
-        int numCmdFail = 0;
-        int numDnf = 0;
-        int numBadData = 0;
-        for (i=0; i<npeers; i++) {
-            String peerId = (String)sources.get(i);
-            try {
-                res = peerGetItem(peerId, uri);
-            } catch (Exception e) {
-                e.printStackTrace();
-                numCmdFail += 1;
-                continue;
-            }
-            // got some kind of response back from peer
-            String status = (String)res.get("status");
-            if (status.equals("ok")) {
-
-                // don't trust at face value!
-                // hash the data, ensure it matches dataHash
-                Hashtable metadata = (Hashtable)res.get("metadata");
-                String dataHash = (String)metadata.get("dataHash");
-                byte [] data = (byte[])res.get("data");
-                String dataHash1 = sha256Base64(data);
-                if (dataHash.equals(dataHash1)) {
-                    // at least the data matches, trust in this vers
-                    // TODO - verify metadata hash against main uri
-
-                    // cache it into local datastore
-                    QDataItem item = new QDataItem(metadata, data);
-                    localPutItem(item);
-                    
-                    // and pass back
-                    return res;
-                }
-                else {
-                    System.out.println("getItem: bad hash on "+data.length+"-byte uri "+uri);
-                    System.out.println("getItem: expected: "+dataHash);
-                    System.out.println("getItem: received: "+dataHash1);
-                    System.out.println("getItem: metadata="+metadata);
-                    numBadData += 1;
-                }
-            }
-            else {
-                numDnf += 1;
-            }
-        }
-
-        // if we get here, then all peers either failed,
-        // returned dnf, or sent back dodgy data
-        res = new Hashtable();
-        res.put("status", "error");
-        res.put("error", "notfound");
-        res.put("summary", 
-                "tried "+npeers+" peers, "
-                    +numCmdFail+" cmdfail, "
-                    +numDnf+" notfound, "
-                    +numBadData+" baddata"
-                );
-        return res;
-    }
-
-    /**
-     * Schedules the insertion of a qsite
-     * @param privKey64 base64 representation of a signed space private key
-     * @param siteName short text name of the qsite, whose URI will end up
-     * as 'Q:pubKey64/siteName/'.
-     * @param rootPath physical absolute pathname of the qsite's root directory
-     * on the host filesystem.
-     * Note that this directory must have a file called 'index.html' at its top
-     * level, which will be used as the qsite's default document.
-     * @param metadata A set of metadata to associate with the qsite
-     * @return Hashtable containing results, as the keys:
-     * <ul>
-     *  <li>status - String - either "ok" or "error"</li>
-     *  <li>error - String - short summary of error, only present if
-     *    status is "error"</li>
-     *  <li>uri - the full Q URI for the top level of the site
-     * </ul>
-     */
-    public Hashtable insertQSite(String privKey64,
-                                 String siteName,
-                                 String rootPath,
-                                 Hashtable metadata
-                                 )
-        throws Exception
-    {
-        // for results
-        Hashtable result = new Hashtable();
-        String uri = null;      // uri under which this site will be reachable
-        String pubKey64;
-
-        File dir = new File(rootPath);
-
-        // barf if no such directory
-        if (!dir.isDirectory()) {
-            result.put("status", "error");
-            result.put("error", "nosuchdir");
-            result.put("detail", "Path '"+rootPath+"' is not a directory");
-            return result;
-        }
-
-        // barf if not readable
-        if (!dir.canRead()) {
-            result.put("status", "error");
-            result.put("error", "cantread");
-            result.put("detail", "Path '"+rootPath+"' is not readable");
-            return result;
-        }
-        
-        // barf if missing or invalid site name
-        siteName = siteName.trim();
-        if (!siteName.matches("[a-zA-Z0-9_-]+")) {
-            result.put("status", "error");
-            result.put("error", "badsitename");
-            result.put("detail", "QSite name should be only alphanumerics, '-' and '_'");
-            return result;
-        }
-
-        String defaultPath = rootPath + sep + "index.html";
-        File defaultFile = new File(defaultPath);
-        
-        // barf if index.html not present and readable
-        if (!(defaultFile.isFile() && defaultFile.canRead())) {
-            result.put("status", "error");
-            result.put("error", "noindex");
-            result.put("detail", "Required file index.html missing or unreadable");
-            return result;
-        }
-
-        // derive public key and uri for site, barf if bad key
-        try {
-            pubKey64 = QUtil.privateToPubHash(privKey64);
-        } catch (Exception e) {
-            result.put("status", "error");
-            result.put("error", "badprivkey");
-            return result;
-        }
-        uri = "Q:" + pubKey64 + "/" + siteName + "/";
-
-        // now the fun recursive bit
-        insertQSiteDir(privKey64, siteName, rootPath, "");
-
-        // queue up an insert of default file
-        metadata.put("type", "qsite");
-        metadata.put("path", siteName+"/");
-        metadata.put("mimetype", "text/html");
-
-        //System.out.println("insertQSite: privKey='"+privKey64+"'");
-        //System.out.println("insertQSite: siteName='"+siteName+"'");
-        //System.out.println("insertQSite: rootDir='"+rootPath+"'");
-        //System.out.println("insertQSite: metadata="+metadata);
-        //System.out.println("insertQSite: default="+defaultPath);
-
-        insertQSiteFile(privKey64, siteName, defaultPath, "", metadata);
-
-        result.put("status", "ok");
-        result.put("uri", uri);
-        return result;
-    }
-
-    /**
-     * recursively queues jobs for the insertion of a directory's contents, for
-     * a qsite.
-     * @param privKey64 - private 'signed space' key, base64 format
-     * @param siteName - short text name for the site
-     * @param absPath - physical pathname of the subdirectory to insert
-     * @param relPath - qsite-relative pathname of this item
-     */
-    protected void insertQSiteDir(String privKey64, String siteName, String absPath, String relPath)
-        throws Exception
-    {
-        File dir = new File(absPath);
-
-        // fail gracefully if not a readable directory
-        if (!(dir.isDirectory() && dir.canRead())) {
-            System.out.println("insertQSiteDir: not a readable directory "+absPath);
-            return;
-        }
-
-        //System.out.println("insertQSiteDir: entry - abs='"+absPath+"' rel='"+relPath+"'");
-
-        // loop through the contents
-        String [] contents = dir.list();
-        for (int i=0; i<contents.length; i++) {
-            String item = contents[i];
-            String itemAbsPath = absPath + sep + item;
-            String itemRelPath = relPath + item;
-            //System.out.println("insertQSiteDir: item='"+item+"' abs='"+itemAbsPath+"' rel='"+itemRelPath+"'");
-            File itemFile = new File(itemAbsPath);
-            
-            // what kind of entry is this?
-            if (itemFile.isDirectory()) {
-                // directory - recursively insert
-                insertQSiteDir(privKey64, siteName, itemAbsPath, itemRelPath + "/");
-
-            } else {
-                // file - insert
-                insertQSiteFile(privKey64, siteName, itemAbsPath, itemRelPath, null);
-            }
-        }
-    }
-
-    /**
-     * queues up the insertion of an individual qsite file
-     * @param privKey64 - base64 signed space private key
-     * @param siteName - name of qsite
-     * @param absPath - absolute location of file on host filesystem
-     * @param relPath - pathname of file relative to Q uri
-     */
-    protected void insertQSiteFile(String privKey64, String siteName,
-                                   String absPath, String relPath, Hashtable metadata)
-        throws Exception
-    {
-        //System.out.println("insertQSiteFile: priv="+privKey64+" name="+siteName+" abs="+absPath+" rel="+relPath+" metadata="+metadata);
-
-        if (metadata == null) {
-            metadata = new Hashtable();
-        }
-
-        metadata.put("privateKey", privKey64);
-        if (!metadata.containsKey("path")) {
-            metadata.put("path", siteName + "/" + relPath);
-        }
-        if (!metadata.containsKey("mimetype")) {
-            metadata.put("mimetype", Mimetypes.guessType(relPath));
-        }
-
-        scheduleLocalInsertJob(absPath, metadata);
-    }
-
-    
-    /**
-     * @param args the command line arguments
-     */
-    public static void main(String[] args)
-        throws IOException, DataFormatException, I2PException, InterruptedException
-    {
-
-        // just for testing
-        System.setProperty("i2cp.tcp.host", "10.0.0.1");
-        
-        QClientNode node;
-        if (args.length > 0) {
-            node = new QClientNode(args[0]);
-        }
-        else {
-            node = new QClientNode();
-        }
-        node.log.info("QClientNode: running node...");
-        node.run();
-    }
-
-    public void foo1() {
-        System.out.println("QClientNode.foo: isClient="+isClient);
-    }
-
-    
-}
diff --git a/apps/q/java/src/net/i2p/aum/q/QClientWebInterface.java b/apps/q/java/src/net/i2p/aum/q/QClientWebInterface.java
deleted file mode 100644
index 72b5bd857fdbc1bb0d68ea55472f5b1f74768d3b..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QClientWebInterface.java
+++ /dev/null
@@ -1,755 +0,0 @@
-/*
- * QClientWebInterface.java
- *
- * Created on April 9, 2005, 1:10 PM
- */
-
-package net.i2p.aum.q;
-
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.net.Socket;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Vector;
-
-import net.i2p.aum.http.HtmlPage;
-import net.i2p.aum.http.I2PHttpRequestHandler;
-import net.i2p.aum.http.MiniHttpServer;
-import HTML.Template;
-
-
-/**
- * Request handler for Q Client nodes that listens within I2P
- * on the client node's destination. Intended for access via
- * eepProxy, and by adding a hosts.txt entry for this dest
- * under the hostname 'q'.
- */
-public class QClientWebInterface extends I2PHttpRequestHandler {
-
-    /** set this to true when debugging html layout */
-    public static boolean loadTemplateWithEachHit = true;
-
-    public QNode node = null;
-
-    // refs to main page template, and components of main page
-    static Template tmplt;
-    static Vector tabRow;
-    static Vector pageItems;
-
-    /**
-     * for security - disables direct-uri GETs of content if running directly over TCP;
-     * we need to coerce users to use their eepproxy browser instead
-     */
-    public boolean isRunningOverTcp = true;
-
-    /** Creates a new instance of QClientWebInterface */
-    public QClientWebInterface(MiniHttpServer server, Object socket, Object node)
-        throws Exception
-    {
-        super(server, socket, node);
-        this.node = (QNode)node;
-        isRunningOverTcp = socket.getClass() == Socket.class;
-    }
-
-    static String [] tabNames = {
-        "home", "search", "insert", "tools", "status", "jobs", "help", "about"
-    };
-
-    /**
-     * Loads a template of a given name. Invokes method on node
-     * to resolve this to an absolute pathname, so 'name' -&gt; '/path/to/html/name.html'
-     */
-    public Template loadTemplate(String name) throws Exception {
-
-        String fullPath = node.getResourcePath("html"+node.sep+name)+".html";
-        //System.out.println("fullPath='"+fullPath+"'");
-        String [] args = new String [] {
-                "filename",  fullPath,
-                "case_sensitive", "true",
-                "max_includes",   "5"
-            };
-        return new Template(args);
-    }
-
-    // ----------------------------------------------------
-    // FRONT-END METHODS
-    // ----------------------------------------------------
-
-    /** GET and POST both go through .safelyHandleReq() */
-    public void on_GET() {
-
-        safelyHandleReq();
-    }
-
-    /** GET and POST both go through .safelyHandleReq() */
-    public void on_POST() {
-
-        safelyHandleReq();
-    }
-
-    public void on_RPC() {
-        
-    }
-
-    /**
-     * wrap .handleReq() - on exception, call dump_error() to
-     * generate a 400 error page with diagnostics
-     */
-    public void safelyHandleReq() {
-        try {
-            handleReq();
-        } catch (Exception e) {
-            dump_error(e);
-        }
-    }
-
-    /**
-     * <p>Forwards hits to either a path handler method, or generic get method.</p>
-     * 
-     * <p>Detects hits to paths for which we have a handler (ie, methods
-     * of this class with name 'hdlr_&lt;somepath&gt;', (such as 'hdlr_help'
-     * for handling hits to '/help').</p>
-     * 
-     * <p>If we have a handler, forward to it, otherwise forward to standard
-     * getItem() method</p>
-     */
-    public void handleReq() throws Exception {
-
-        Class [] noArgs;
-        Method hdlrMethod;
-
-        // strip useless leading slash from reqFile
-        reqFile = reqFile.substring(1);
-
-        // default to 'home'
-        if (reqFile.equals("")) {
-            reqFile = "home";
-        }
-        //print("handleReq: reqFile='"+reqFile+"'");
-        
-        // Set up the main page template
-        try {
-            tmplt = loadTemplate("main");
-            pageItems = new Vector();
-            tmplt.setParam("items", pageItems);
-            tmplt.setParam("nodeType", node.nodeType);
-
-        } catch (Exception e) {
-            e.printStackTrace();
-            throw e;
-        }
-        //print("handleReq: loaded template");
-
-        // execute if a command
-        if (allVars.containsKey("cmd")) {
-            do_cmd();
-        }
-
-        // --------------------------------------------------------
-        // intercept magic paths for which we have a handler method
-        noArgs = new Class[0];
-        try {
-            // extract top dir of path and make it method-name-safe
-            String methodName = "hdlr_"+reqFile.split("/")[0].replace('.','_');
-            hdlrMethod = this.getClass().getMethod(methodName, null);
-
-            // now dispatch the method
-            hdlrMethod.invoke(this, null);
-
-            // spit out html, if no raw content inserted
-            sendPageIfNoContent();
-            
-            // done
-            return;
-
-        } catch (NoSuchMethodException e) {
-            // routinely fails if we dont' have handler, so assume it's
-            // a GET
-        }
-        
-        // if we get here, client is requesting a specific uri
-        allVars.put("uri", reqFile);
-        if (!cmd_get()) {
-            hdlr_home();
-        }
-        sendPageIfNoContent();
-    }
-
-    /**
-     * as name implies, generates standard html page
-     * if setRawOutput hasnt' been called
-     */
-    public void sendPageIfNoContent() {
-
-        if (rawContentBytes == null) {
-            
-            // we're spitting out html
-            setContentType("text/html");
-            
-            // set up tab row style vector
-            setupTabRow();
-            
-            // finally, render out our filled-out template
-            setRawOutput(tmplt.output());
-        }
-    }
-
-    /**
-     * Inserts an item into main pane
-     */
-    public Object addToMainPane(Object item) {
-        
-        Hashtable h = new Hashtable();
-        h.put("item", item);
-        pageItems.addElement(h);
-        return item;
-    }
-
-    /**
-     * Generates a set of tabs and adds these to the page,
-     * marking as active the tab whose name is in the current URL
-     */
-    public void setupTabRow()
-    {
-        Hashtable h;
-        tabRow = new Vector();
-        for (int i=0; i< tabNames.length; i++) {
-            String name = tabNames[i];
-            h = new Hashtable();
-            h.put("name", name);
-            h.put("label", name.substring(0,1).toUpperCase()+name.substring(1));
-            if (name.equals(reqFile)) {
-                h.put("active", "1");
-            }
-        tabRow.addElement(h);
-        tmplt.setParam("tabs", tabRow);
-        }
-    }
-
-    // -----------------------------------------------------
-    // METHODS FOR HANDLING MAGIC PATHS
-    // ----------------------------------------------------
-    
-    /** Display home page */
-    public void hdlr_home() throws Exception {
-
-        // stick in 'getitem' form
-        addToMainPane(loadTemplate("getform"));
-
-    }
-
-    /** Display status page */
-    public void hdlr_status() throws Exception {
-        
-        // ping the node, extract status items
-        Vector statusItems = new Vector();
-        Hashtable h = node.ping();
-        for (Enumeration e = h.keys(); e.hasMoreElements();) {
-            String key = (String)e.nextElement();
-            String val = h.get(key).toString();
-            if (val.length() > 60) {
-                // too big for table, stick into a readonly text field
-                val = "<input type=text size=60 readonly name=\"big_"+key+"\" value=\""+val+"\">";
-            }
-            Hashtable rec = new Hashtable();
-            rec.put("key", key);
-            rec.put("value", val);
-            //print("key='"+key+"' val='"+val+"'");
-            statusItems.addElement(rec);
-        }
-
-        // get status form template insert the items, stick onto main pane
-        Template tmpltStatus = loadTemplate("status");
-        tmpltStatus.setParam("items", statusItems);
-        addToMainPane(tmpltStatus);
-
-    }
-
-    /** display current node jobs list */
-    public void hdlr_jobs() throws Exception {
-
-        // get jobs list, add to jobs list template, add that to main pane
-        Template tmpltJobs = loadTemplate("jobs");
-        tmpltJobs.setParam("items", node.getJobsList());
-        addToMainPane(tmpltJobs);
-    }
-
-    /** Display search form */
-    public void hdlr_search() throws Exception {
-        addToMainPane(loadTemplate("searchform"));
-    }
-
-    /** Display insert page */
-    public void hdlr_insert() throws Exception {
-
-        String formName = allVars.get("mode", 0, "file").equals("site") ? "putsiteform" : "putform";
-        Template tmpltPut = loadTemplate(formName);
-        addToMainPane(tmpltPut);
-    }
-
-    /** Display settings screen */
-    public void hdlr_settings() throws Exception {
-        addToMainPane(loadTemplate("settings"));
-    }
-
-    /** Display tools screen */
-    public void hdlr_tools() throws Exception {
-
-        addToMainPane(loadTemplate("tools"));
-        addToMainPane(loadTemplate("genkeysform"));
-        addToMainPane(loadTemplate("addrefform"));
-    }
-
-    /** Display help screen */
-    public void hdlr_help() throws Exception {
-        addToMainPane(loadTemplate("help"));
-    }
-
-    /** Display about screen */
-    public void hdlr_about() throws Exception {
-        addToMainPane(loadTemplate("about"));
-    }
-
-    /** handle /favicon.ico hits */
-    public void hdlr_favicon_ico() {
-
-        System.out.println("Sending favicon image");
-        setContentType("image/x-icon");
-        setRawOutput(Favicon.image);
-    }
-
-    /** dummy handler, causes an exception (for testing error dump pages */
-    public void hdlr_shit() throws Exception {
-        throw new Exception("this method is shit");
-    }
-
-    // ----------------------------------------------------
-    // METHODS FOR HANDLING COMMANDS
-    // ----------------------------------------------------
-
-    /**
-     * invoked if GET or POST vars contain 'cmd'.
-     * attempts to dispatch command handler method 'cmd_xxxx'
-     */
-    public void do_cmd() throws Exception {
-
-        // this whole method could be done in python with the statement:
-        //   getattr(self, 'cmd_'+urlVars['cmd'], lambda:None)()
-        String cmd = allVars.get("cmd", 0);
-        try {
-            // extract top dir of path and make it method-name-safe
-            String methodName = "cmd_"+cmd;
-            Method hdlrMethod = this.getClass().getMethod(methodName, null);
-
-            // now dispatch the method
-            hdlrMethod.invoke(this, null);
-        } catch (NoSuchMethodException e) {}
-    }
-
-
-    /**
-     * executes a 'get' cmd
-     */
-    public boolean cmd_get() throws Exception {
-        
-        Hashtable result = null;
-        String status = null;
-        Hashtable metadata = null;
-        String mimetype = null;
-        
-        // bail if node offline
-        if (node == null) {
-            return false;
-        }
-        
-        // bail if no 'url' arg
-        if (!allVars.containsKey("uri")) {
-            return false;
-        }
-        
-        // get uri, prepend 'Q:' if needed
-        String uri = allVars.get("uri", 0);
-        if (!uri.startsWith("Q:")) {
-            uri = "Q:" + uri;
-        }
-
-        // attempt the fetch
-        result = node.getItem(uri);
-        status = (String)result.get("status");
-
-        // how'd we go?
-        if (status.equals("ok")) {
-            // got it - send it back
-            metadata = (Hashtable)result.get("metadata");
-            mimetype = (String)metadata.get("mimetype");
-
-            // forbid content retrieval via MSIE
-            boolean isIE = false;
-            for (Enumeration e = headerVars.get("User-Agent").elements(); e.hasMoreElements();) {
-                String val = ((String)e.nextElement()).toLowerCase();
-                if (val.matches(".*(msie|windows|\\.net).*")) {
-                    Template warning = loadTemplate("msiealert");
-                    addToMainPane(warning);
-                    return false;
-                }
-            }
-            
-            // forbid direct delivery of text/* content via direct tcp
-            if (isRunningOverTcp) {
-                // security feature - set to application/octet-stream if req arrives via tcp.
-                // this prevents people surfing the q web interface directly over TCP and
-                // falling prey to anonymity attacks (eg gif bugs)
-                
-                // if user is trying to hit an html page, we can send back a warning
-                if (mimetype.startsWith("text")) {
-                    Template warning = loadTemplate("anonalert");
-                    warning.setParam("dest", node.destStr);
-                    addToMainPane(warning);
-                    return false;
-                }
-                setContentType("application/octet-stream");
-            } else {
-                // got this conn via I2P and eeproxy - safer to obey the mimetype
-                setContentType(mimetype);
-            }
-
-            setRawOutput((byte [])result.get("data"));
-            return true;
-        } else {
-            // 404
-            tmplt.setParam("show_404", "1");
-            tmplt.setParam("404_uri", uri);
-            return false;
-        }
-    }
-
-    /** executes genkeys command */
-    public void cmd_genkeys() throws Exception {
-        
-        Hashtable res = node.newKeys();
-        String pubKey = (String)res.get("publicKey");
-        String privKey = (String)res.get("privateKey");
-        Template keysWidget = loadTemplate("genkeysresult");
-        keysWidget.setParam("publickey", pubKey);
-        keysWidget.setParam("privatekey", privKey);
-        addToMainPane(keysWidget);
-    }
-    
-    /** adds a noderef */
-    public void cmd_addref() throws Exception {
-
-        String ref = allVars.get("noderef", 0).trim();
-        node.hello(ref);
-    }
-
-    /** executes 'put' command */
-    public void cmd_put() throws Exception {
-
-        // barf if user posted both data and rawdata
-        if (allVars.containsKey("data")
-            && ((String)allVars.get("data", 0)).length() > 0
-            && allVars.containsKey("rawdata")
-            && ((String)allVars.get("rawdata", 0)).length() > 0
-            )
-        {
-            Template t = loadTemplate("puterror");
-            t.setParam("error", "you specified a file as well as 'rawdata'");
-            addToMainPane(t);
-            addToMainPane(dumpVars().toString());
-            return;
-        }
-
-        Hashtable metadata = new Hashtable();
-        byte [] data = new byte[0];
-        
-        // stick in some defaults
-        String [] keys = {
-            "data", "rawdata",
-            "mimetype", "keywords", "privkey", "abstract", "type", "title",
-            "path"
-        };
-
-        //System.out.println("allVars='"+allVars+"'");
-
-        // extract all items from form, add to metadata ones that
-        // have non-zero length. Take 'data' or 'rawdata' and stick their
-        // bytes into data.
-        for (int i=0; i<keys.length; i++) {
-            String key = keys[i];
-            if (allVars.containsKey(key)) {
-                //System.out.println("posted item '"+key+"'");
-
-                if (key.equals("data")) {
-                    byte [] dataval = allVars.get("data", 0).getBytes();
-                    if (dataval.length > 0) {
-                        data = dataval;
-                    }
-                } else if (key.equals("rawdata")) {
-                    byte [] dataval = allVars.get("rawdata", 0).getBytes();
-                    if (dataval.length > 0) {
-                        data = dataval;
-                    }
-                } else if (key.equals("privkey")) {
-                    String k = allVars.get("privkey", 0);
-                    if (k.length() > 0) {
-                        metadata.put("privateKey", k);
-                    }
-                } else {
-                    String val = allVars.get(key, 0);
-                    //System.out.println("'"+key+"'='"+val+"'");
-                    if (val.length() > 0) {
-                        metadata.put(key, allVars.get(key, 0));
-                    }
-                }
-            }
-        }
-
-        //System.out.println("metadata="+metadata);
-
-        if (metadata.size() == 0) {
-            Template err = loadTemplate("puterror");
-            err.setParam("error", "No metadata!");
-            addToMainPane(err);
-            addToMainPane(dumpVars().toString());
-            return;
-        }
-
-        if (data.length == 0) {
-            Template err = loadTemplate("puterror");
-            err.setParam("error", "No data!");
-            addToMainPane(err);
-            addToMainPane(dumpVars().toString());
-            return;
-        }
-
-        // phew! ready to put
-        System.out.println("WEB:cmd_put: inserting");
-        
-        Hashtable result = node.putItem(metadata, data);
-        
-        System.out.println("WEB:cmd_put: got"+result);
-
-        String status = (String)result.get("status");
-        if (!status.equals("ok")) {
-            String errTxt = (String)result.get("error");
-            if (result.containsKey("summary")) {
-                errTxt = errTxt + ":" + result.get("summary").toString();
-            }
-            Template err = loadTemplate("puterror");
-            err.setParam("error", (String)result.get("error"));
-            addToMainPane(err);
-            addToMainPane(dumpVars().toString());
-            return;
-        }
-
-        // success, yay!
-        Template success = loadTemplate("putok");
-        success.setParam("uri", (String)result.get("uri"));
-        addToMainPane(success);
-
-        //System.out.println("cmd_put: debug on page??");
-        //addToMainPane(dumpVars().toString());
-    }
-
-    /** executes 'putsite' command */
-    public void cmd_putsite() throws Exception {
-
-        Hashtable metadata = new Hashtable();
-        String privKey = allVars.get("privkey", 0, "");
-        String name = allVars.get("name", 0, "");
-        String dir = allVars.get("dir", 0, "");
-
-        // pick up optional metadata items
-        String [] keys = {
-            "title", "keywords", "abstract",
-        };
-
-        // extract all items from form, add to metadata ones that
-        // have non-zero length.
-        for (int i=0; i<keys.length; i++) {
-            String key = keys[i];
-            if (allVars.containsKey(key)) {
-                //System.out.println("posted item '"+key+"'");
-                String val = allVars.get(key, 0);
-                //System.out.println("'"+key+"'='"+val+"'");
-                if (val.length() > 0) {
-                    metadata.put(key, allVars.get(key, 0));
-                }
-            }
-        }
-
-        //System.out.println("metadata="+metadata);
-
-        if (metadata.size() == 0) {
-            cmd_putsite_error("No metadata!");
-            return;
-        }
-
-        // phew! ready to put
-        Hashtable result = node.insertQSite(privKey, name, dir, metadata);
-        String status = (String)result.get("status");
-        if (!status.equals("ok")) {
-            cmd_putsite_error((String)result.get("error"));
-            return;
-        }
-
-        // success, yay!
-        Template success = loadTemplate("putok");
-        success.setParam("is_site", "1");
-        success.setParam("uri", (String)result.get("uri"));
-        addToMainPane(success);
-
-        //System.out.println("cmd_put: debug on page??");
-        //addToMainPane(dumpVars().toString());
-    }
-
-    protected void cmd_putsite_error(String msg) throws Exception {
-        
-        Template err = loadTemplate("puterror");
-        err.setParam("error", msg);
-        err.setParam("is_site", "1");
-        addToMainPane(err);
-        addToMainPane(dumpVars().toString());
-    }
-
-    /** performs a search */
-    public void cmd_search() throws Exception {
-
-        Hashtable criteria = new Hashtable();
-        String [] fields = {
-            "type", "title", "path", "mimetype", "keywords",
-            "summary", "searchmode"
-        };
-
-        for (int i=0; i<fields.length; i++) {
-            String fieldName = fields[i];
-            String fieldVal = allVars.get(fieldName, 0, null);
-            if (fieldVal == null) {
-                continue;
-            }
-
-            if (fieldName.equals("type") && fieldVal.equals("any")) {
-                continue;
-            }
-
-            if (!fieldVal.equals("")) {
-                if (!(fieldName.equals("searchmode") || allVars.containsKey(fieldName+"_re"))) {
-                    // convert into a regexp which matches exact substring
-                    fieldVal = ".*\\Q" + fieldVal + "\\E.*";
-                }
-                criteria.put(fieldName, fieldVal);
-            }
-        }
-
-        //addToMainPane("Search criteria: "+criteria);
-        
-        Hashtable result = node.search(criteria);
-        Vector items = (Vector)result.get("items");
-
-        // stick up search results form
-        Template results = loadTemplate("searchresults");
-        System.out.println("items="+items);
-        results.setParam("results", items);
-        results.setParam("numresults", items.size());
-        addToMainPane(results);
-        
-        //addToMainPane(dumpVars().toString());
-    }
-
-
-    // -----------------------------------------------------
-    // NODE INTERACTION METHODS
-    // -----------------------------------------------------
-
-    public void do_get(String uri) {
-
-        // prefix uri with 'Q:' if needed
-        if (!uri.startsWith("Q:")) {
-            uri = "Q:"+uri;
-        }
-
-        print("GET "+uri);
-    }
-
-    // ----------------------------------------------------
-    // DIAGNOSTICS METHODS
-    // ----------------------------------------------------
-    
-    /**
-     * Sends back a 400 status, together with a disgnostic page
-     * showing the stack dump and the HTTP url and form variables
-     */
-    public void dump_error(Throwable e) {
-
-        // un-wrap InvocationMethodException
-        if (e instanceof InvocationTargetException) {
-            Throwable e1 = e.getCause();
-            if (e1 != null) {
-                e = e1;
-            }
-        }
-
-        // set up barf reply
-        setStatus("HTTP/1.0 400 Error");
-
-        setContentType("text/html");
-
-        // render the exception into raw string
-        ByteArrayOutputStream os = new ByteArrayOutputStream();
-        PrintStream ps = new PrintStream(os);
-        e.printStackTrace(ps);
-        String eText = new String(os.toByteArray());
-
-        // generate an html page
-        HtmlPage page = new HtmlPage();
-        page.head.add("title").raw("Q Error");
-
-        // now dump out the trace, and our HTTP vars
-        page.body
-            .nest("h3")
-                .nest("i")
-                    .raw("Dammit!!")
-                    .end
-                .end
-            .hr()
-            .raw("There was an internal error processing your request")
-            .br().hr()
-            .nest("code")
-                .nest("pre")
-                    .raw(eText)
-                    .end
-                .end
-            .hr()
-            .add(dumpVars())
-            ;
-
-        setRawOutput(page.toString());
-        e.printStackTrace();
-    }
-
-    public static void print(String arg) {
-        System.out.println("QClientWebInterface: "+arg);
-    }
-
-    /**
-     * run up this web interface on a local port, for testing
-     */
-    public static void main(String [] args) {
-
-        // just for testing
-        System.setProperty("i2cp.tcp.host", "10.0.0.1");
-
-        try {
-            QClientNode node = new QClientNode();
-            //node.start();
-            node.run();
-
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/q/QConsole.java b/apps/q/java/src/net/i2p/aum/q/QConsole.java
deleted file mode 100644
index b2fbec0f8d181a4704c4e443c8687ffd1e84553f..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QConsole.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * QConsole.java
- *
- * Created on 19 April 2005, 12:28
- */
-
-package net.i2p.aum.q;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.net.URLDecoder;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Properties;
-import java.util.Vector;
-
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.xmlrpc.XmlRpcClient;
-
-/**
- *
- * @author  david
- * @version
- */
-public class QConsole extends HttpServlet {
-    
-    XmlRpcClient nodeProxy;
-
-    boolean nodeExists = false;
-    boolean nodeIsRunning = false;
-    String nodeDirStr;
-    File nodeDir;
-    String nodePrivKey;
-    String sep = File.separator;
-    String cpsep = File.pathSeparator;
-    Properties options;
-    
-    /** Initializes the servlet.
-     */
-    public void init(ServletConfig config) throws ServletException {
-        super.init(config);
-        try {
-            nodeProxy = new XmlRpcClient("http://localhost:7651");
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        
-        // load config, if any
-        options = new Properties();
-        try {
-            FileInputStream f = new FileInputStream("qconsole.conf");
-            options.load(f);
-            
-        } catch (Exception e) {
-            // no valid config
-            e.printStackTrace();
-            System.out.println("BAD OR MISSING CONF");
-        }
-    }
-    
-    /** Destroys the servlet.
-     */
-    public void destroy() {
-        
-    }
-    
-    /** Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
-     * @param request servlet request
-     * @param response servlet response
-     */
-    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
-    throws ServletException, IOException {
-
-        findNode();
-        determineIfNodeIsRunning();
-
-        Hashtable vars = parseVars(request.getQueryString());
-        
-        response.setContentType("text/html");
-        PrintWriter out = response.getWriter();
-        out.println("<html>");
-        out.println("<head>");
-        out.println("<title>QConsole</title>");
-        out.println("</head>");
-        out.println("<body>");
-
-        out.println("<h1>Q Node Manager</h1>");
-
-        //out.println("debug: vars='"+vars+"'<br><br>");
-
-        if (vars.containsKey("startnode") && !nodeIsRunning) {
-            startNode();
-            if (!nodeIsRunning) {
-                out.println("Failed to start node :(<br><br>");
-            }
-
-        } else if (vars.containsKey("stopnode") && nodeIsRunning) {
-            stopNode();
-            nodeIsRunning = false;
-        }
-        
-        if (nodeIsRunning) {
-            out.println("Q Node is running<br><br>");
-            out.print("<a href=\"http://localhost:7651\">Node Console</a>");
-            out.print(" | ");
-            out.println("<a href=\"/q?stopnode\">Stop Node</a>");
-        } else {
-            out.println("Q Node is not running<br><br>");
-            out.println("<a href=\"/q?startnode\">Start Node</a>");
-        }
-
-        out.println("</body>");
-        out.println("</html>");
-        /* */
-        out.close();
-    }
-    
-    /** Handles the HTTP <code>GET</code> method.
-     * @param request servlet request
-     * @param response servlet response
-     */
-    protected void doGet(HttpServletRequest request, HttpServletResponse response)
-    throws ServletException, IOException {
-        processRequest(request, response);
-    }
-    
-    /** Handles the HTTP <code>POST</code> method.
-     * @param request servlet request
-     * @param response servlet response
-     */
-    protected void doPost(HttpServletRequest request, HttpServletResponse response)
-    throws ServletException, IOException {
-        processRequest(request, response);
-    }
-    
-    /** Returns a short description of the servlet.
-     */
-    public String getServletInfo() {
-        return "Short description";
-    }
-
-    /** try to find node */
-    public void findNode() {
-
-        try {
-            nodeDirStr = System.getProperties().getProperty("user.home")
-                        + sep + ".quartermaster_client";
-                        
-            // yay, found a node (we hope), create an xmlrpc client for talking
-            // to that node
-            String propPath = nodeDirStr + sep + "node.conf";
-            File propFile = new File(propPath);
-            FileInputStream propIn = new FileInputStream(propPath);
-            Properties prop = new Properties();
-            prop.load(propIn);
-
-            nodePrivKey = prop.getProperty("privKey");
-
-            // presence of private key indicates node exists
-            nodeExists = nodePrivKey != null;
-            
-        } catch (Exception e) {
-            // node doesn't exist
-        }
-
-    }
-    
-    public void startNode() {
-        
-        int i;
-        String [] jars = {
-            "i2p", "mstreaming", "aum",
-        };
-        
-        String cp = "";
-
-        String jarsDir = "lib";
-        
-        for (i=0; i<jars.length; i++) {
-            if (i> 0) {
-                cp += cpsep;
-            }
-            cp += jarsDir + sep + jars[i] + ".jar";
-        }
-
-        System.out.println("cp='"+cp+"'");
-        
-        // build up args
-        int nopts = options.size();
-        String args = "";
-        args += "java";
-        for (Enumeration e = options.propertyNames(); e.hasMoreElements();) {
-            String opt = (String)e.nextElement();
-            String arg = "-D" + opt + "=" + options.getProperty(opt);
-            System.out.println(arg);
-            args += " " + arg;
-        }
-            
-        args += " -cp " + cp;
-        args += " net.i2p.aum.q.QMgr";
-        args += " foreground";
-        
-        Runtime runtime = Runtime.getRuntime();
-
-        // and spawn the start job
-        try {
-            //runtime.exec(startForegroundArgs, propLines);
-            System.out.println("args='"+args+"'");
-            runtime.exec(args, null);
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-        
-        // wait a bit
-        sleep(3);
-
-        // try for 10s to contact node
-        for (i=0; i<10; i++) {
-            sleep(1);
-            determineIfNodeIsRunning();
-            if (nodeIsRunning) {
-                break;
-            }
-        }
-    }
-    
-    public void stopNode() {
-
-        Vector args = new Vector();
-        args.addElement(nodePrivKey);
-        try {
-            System.out.println("stopping node...");
-            nodeProxy.execute("i2p.q.shutdown", args);
-        } catch (Exception e) {
-            
-        }
-        System.out.println("node terminated");
-    }
-
-    /** returns true if node is up */
-    public void determineIfNodeIsRunning() {
-        try {
-            nodeProxy.execute("i2p.q.ping", new Vector());
-            nodeIsRunning = true;
-        } catch (Exception e) {
-            nodeIsRunning = false;
-            return;
-        }
-    }
-
-    public void sleep(int n) {
-        try {
-            Thread.sleep(n * 1000);
-        } catch (Exception e) {}
-    }
-
-    public Hashtable parseVars(String raw) {
-        Hashtable h = new Hashtable();
-
-        if (raw == null) {
-            return h;
-        }
-
-        URLDecoder u = new URLDecoder();
-        String [] items = raw.split("[&]");
-        String dec;
-        for (int i=0; i<items.length; i++) {
-            try {
-                dec = u.decode(items[i], "ISO-8859-1");
-                String [] items1 = dec.split("[=]",2);
-                //System.out.println("parseVars: "+items1[0]+"="+items1[1]);
-                h.put(items1[0], items1.length > 1 ? items1[1] : "");
-            } catch (Exception e) {
-                    e.printStackTrace();
-            }
-        }
-        
-        return h;
-    }
-
-}
diff --git a/apps/q/java/src/net/i2p/aum/q/QDataItem.java b/apps/q/java/src/net/i2p/aum/q/QDataItem.java
deleted file mode 100644
index de3ed060d9ba377c38bf1586d11e0e352f8e7735..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QDataItem.java
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * QSSK.java
- *
- * Created on April 4, 2005, 11:35 AM
- */
-
-package net.i2p.aum.q;
-
-import java.io.Serializable;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.TreeSet;
-
-import net.i2p.aum.Mimetypes;
-import net.i2p.crypto.DSAEngine;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Signature;
-import net.i2p.data.SigningPrivateKey;
-import net.i2p.data.SigningPublicKey;
-
-
-/**
- * Singleton class with various static methods
- * for processing and validating metadata
- */
-public class QDataItem extends Hashtable implements Serializable {
-
-    /** set to true to enable verbose QUtil.debug output to stdout */
-    public static boolean _debug = false;
-    //public static boolean _debug = true;
-
-    public byte [] _data;
-
-    public String _path = null;
-
-    /** Number of chars to truncate the 'public SSK key' base64 string to.
-     * This weakens cryptographic security of the SSK, but makes for
-     * slightly less ugly URIs. Set to -1 to disable truncation (and thus
-     * get the full sha256 security
-     */
-    private static final int _pubHashTruncation = -1;
-
-    public QDataItem(Hashtable h, byte [] data) {
-        super(h);
-        _data = data;
-    }
-
-    public QDataItem(Hashtable h) {
-        this(h, null);
-    }
-
-    public QDataItem(byte [] data) {
-        super();
-        _data = data;
-    }
-
-    /**
-     * Monolithic main method - processes a block of metadata according to the
-     * rules in the Q metadata specification
-     * @param metadata a block of metadata, received via a getItem or
-     * putItem request
-     * @param data the raw binary data
-     * @param isFromApp true if this metadata has been freshly passed in from
-     * a Q application, false if it's in the Q works
-     * @throws QException if the metadata is invalid
-     */
-    public void processAndValidate(boolean isClient) throws QException {
-
-        String uri;
-        String ext;
-
-        SigningPrivateKey _privKey = null;
-        SigningPublicKey _pubKey = null;
-
-        // -----------------------------------------
-        // barf if no data
-        if (_data == null) {
-            throw new QException("No data");
-        }
-
-        // -----------------------------------------
-        // barf if privKey given and not from client
-        if (containsKey("privateKey") && !isClient) {
-            throw new QException("Only Q applications can pass in a privateKey");
-        }
-
-        // -----------------------------------------
-        // generate dataHash (or verify existing)
-        if (containsKey("dataHash")) {
-            if (!get("dataHash").equals(QUtil.sha64(_data))) {
-                throw new QException("Invalid dataHash");
-            }
-        } else {
-            put("dataHash", QUtil.sha64(_data));
-        }
-        
-        // -----------------------------------------
-        // generate size, or verify existing
-        if (containsKey("size")) {
-            if (!get("size").toString().equals(String.valueOf(_data.length))) {
-                throw new QException("Invalid size");
-            }
-        } else {
-            put("size", new Integer(_data.length));
-        }
-
-        // -----------------------------------------
-        // default 'title' to dataHash
-        if (!containsKey("title")) {
-            put("title", get("dataHash"));
-        }
-
-        // -----------------------------------------
-        // default the type
-        if (!containsKey("type")) {
-            put("type", "other");
-        }
-        
-        // ------------------------------------------------
-        // default the path
-        if (containsKey("path")) {
-            _path = get("path").toString();
-            if (_path.length() > 0) {
-                if (!_path.startsWith("/")) {
-                    _path = "/" + _path;
-                    put("path", _path);
-                }
-            }
-
-            // determine file extension
-            String [] bits = _path.split("/");
-            String name = bits[bits.length-1];
-            bits = name.split("\\.", 2);
-            ext = "." + bits[bits.length-1];
-        }
-        else {
-            // path is empty - set to '/<dataHash>.ext' where 'ext' is the
-            // file extension guessed from present mimetype value, and dataHash
-            // is a shortened hash of the content
-            String mime = (String)get("mimetype");
-            if (mime == null) {
-                mime = "application/octet-stream";
-                put("mimetype", mime);
-            }
-
-            // determine file extension
-            ext = Mimetypes.guessExtension(mime);
-
-            // and determine final path
-            _path = "/" + ((String)get("dataHash")).substring(0, 10) + ext;
-            put("path", _path);
-        }
-
-        // -----------------------------------------
-        // default the mimetype
-        if (!containsKey("mimetype")) {
-            String mimetype = Mimetypes.guessType(ext);
-            put("mimetype", mimetype);
-        }
-
-        // ------------------------------------------
-        // barf if contains mutually-exclusive signed space keys
-        if (containsKey("privateKey") && (containsKey("publicKey") || containsKey("signature"))) {
-            throw new QException("Metadata must NOT contain privateKey and one of publicKey or signature");
-        }
-
-        // ------------------------------------------
-        // barf if exactly one of publicKey and signature are present
-        if (containsKey("publicKey") ^ containsKey("signature")) {
-            throw new QException("Either both or neither of 'publicKey' and 'signature' must be present");
-        }
-
-        // -----------------------------------------
-        // now discern between plain hash items and 
-        // signed space items
-        if (containsKey("privateKey") || containsKey("publicKey")) {
-
-            DSAEngine dsa = DSAEngine.getInstance();
-
-            // process/validate remaining data in signed space context
-
-            if (containsKey("privateKey")) {
-                // only private key given - uplift, remove, replace with public key
-                _privKey = new SigningPrivateKey();
-                String priv64 = get("privateKey").toString();
-                try {
-                    _privKey.fromBase64(priv64);
-                } catch (Exception e) {
-                    throw new QException("Invalid privateKey", e);
-                }
-
-                // ok, got valid privateKey
-                
-                // expunge privKey from metadata, replace with publicKey
-                this.remove("privateKey");
-                _pubKey = _privKey.toPublic();
-                put("publicKey", _pubKey.toBase64());
-
-                // create and insert a signature
-                QUtil.debug("before sig, asSortedString give:\n"+asSortedString());
-
-                Signature sig = dsa.sign(asSortedString().getBytes(), _privKey);
-                String sigBase64 = sig.toBase64();
-                put("signature", sigBase64);
-            }
-            else {
-                // barf if not both signature and pubkey present
-                if (!(containsKey("publicKey") && containsKey("signature"))) {
-                    throw new QException("need both publicKey and signature");
-                }
-                _pubKey = new SigningPublicKey();
-                String pub64 = get("publicKey").toString();
-                try {
-                    _pubKey.fromBase64(pub64);
-                } catch (Exception e) {
-                    throw new QException("Invalid publicKey", e);
-                }
-            }
-
-            // now, whether we just signed or not, validate the signature/pubkey
-            byte [] thisAsBytes = asSortedString().getBytes();
-
-            String sig64 = get("signature").toString();
-            Signature sig1 = new Signature();
-            try {
-                sig1.fromBase64(sig64);
-            } catch (DataFormatException e) {
-                throw new QException("Invalid signature string", e);
-            }
-
-            if (!dsa.verifySignature(sig1, thisAsBytes, _pubKey)) {
-                throw new QException("Invalid signature");
-            }
-
-            // last step - determine the correct URI
-            String pubHash = QUtil.hashPubKey(_pubKey);
-            uri = "Q:"+pubHash+_path;
-            
-        }       // end of 'signed space' mode processing
-        else {
-            // -----------------------------------------------------
-            // process/validate remaining data in plain hash context
-            String thisHashed = QUtil.sha64(asSortedString());
-            uri = "Q:"+ thisHashed + ext;
-
-        }       // end of plain hash mode processing
-
-        
-        // -----------------------------------------------------
-        // final step - add or validate uri
-        if (containsKey("uri")) {
-            if (!get("uri").toString().equals(uri)) {
-                throw new QException("Invalid URI");
-            }
-        } else {
-            put("uri", uri);
-        }
-        
-    }
-
-    /**
-     * returns a filename under which this item should be stored
-     */
-    public String getStoreFilename() throws QException {
-        if (!containsKey("uri")) {
-            throw new QException("Missing URI");
-        }
-        return QUtil.sha64((String)get("uri"));
-    }
-
-    /**
-     * Hashes this set of metadata, excluding any 'signature' key
-     * @return Base64 hash of metadata
-     */
-    public String hashThisAsBase64() {
-
-        return QUtil.sha64(asSortedString());
-    }
-
-    public byte [] hashThis() {
-
-        return QUtil.sha(asSortedString());
-    }
-
-    /**
-     * alphabetise thie metadata to a single string, containing one
-     * 'key=value' entry per line. Excludes keys 'uri' and 'signature'
-     */
-    public String asSortedString() {
-
-        TreeSet t = new TreeSet(keySet());
-        Iterator keys = t.iterator();
-        int nkeys = t.size();
-        int i;
-        String metaStr = "";
-        for (i = 0; i < nkeys; i++)
-        {
-            String metaKey = (String)keys.next();
-            if (!(metaKey.equals("signature") || metaKey.equals("uri"))) {
-                metaStr += metaKey + "=" + get(metaKey) + "\n";
-            }
-        }
-        return metaStr;
-    }
-
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/q/QException.java b/apps/q/java/src/net/i2p/aum/q/QException.java
deleted file mode 100644
index fcb58aee00aca7b857803dc2b037962e579ba1f7..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QException.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * QException.java
- *
- * Created on April 6, 2005, 2:05 PM
- */
-
-package net.i2p.aum.q;
-
-import java.io.PrintStream;
-import java.io.PrintWriter;
-
-/**
- * Base class of Q exceptions
- * @author jrandom (shamelessly rebadged by aum)
- */
-
-public class QException extends Exception {
-    private Throwable _source;
-
-    public QException() {
-        this(null, null);
-    }
-
-    public QException(String msg) {
-        this(msg, null);
-    }
-
-    public QException(String msg, Throwable source) {
-        super(msg);
-        _source = source;
-    }
-
-    public void printStackTrace() {
-        if (_source != null) _source.printStackTrace();
-        super.printStackTrace();
-    }
-
-    public void printStackTrace(PrintStream ps) {
-        if (_source != null) _source.printStackTrace(ps);
-        super.printStackTrace(ps);
-    }
-
-    public void printStackTrace(PrintWriter pw) {
-        if (_source != null) _source.printStackTrace(pw);
-        super.printStackTrace(pw);
-    }
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/q/QIndexFile.java b/apps/q/java/src/net/i2p/aum/q/QIndexFile.java
deleted file mode 100644
index 0df2411a45def57ed4afb114191366057e88e68a..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QIndexFile.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * QIndexFile.java
- *
- * Created on March 24, 2005, 11:55 AM
- */
-
-package net.i2p.aum.q;
-
-import java.io.File;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.util.Date;
-import java.util.Iterator;
-
-/**
- * <p>Implements a binary-searchable file for storing (time, hash) records.
- * This makes it faster for server nodes to determine which content entries,
- * catalog entries and peer entries have appeared since time t.</p>
- *
- * <p>To ease inter-operation with other programs, as well as human troubleshooting,
- * The file is implemented as a plain text file, with records in the
- * following format:
- *   <ul>
- *     <li><b>time</b> unixtime, as 10-byte decimal string</li>
- *     <li><b>=</b> single-char delimiter</li>
- *     <li><b>hash</b> - a 44-byte Base64 representation of an sha256 hash</li>
- *   </ul>
- * </p>
- */
-public class QIndexFile {
-    
-    public String path;
-    File fileObj;
-    RandomAccessFile file;
-    public long rawLength;
-    public int numRecs;
-    FileReader reader;
-    FileWriter writer;
-    
-    /** length of base64 representation of sha256 hash */
-    static public int hashLen = 43;
-
-    /** length of unixtime milliseconds in decimal format */
-    static public int timeLen = 13;
-
-    /**
-     * length of records, allowing for time field, delimter (,),
-     * hash field and terminating newline
-     */
-    static public int recordLen = hashLen + timeLen + 2;
-    
-    /**
-     * Create a new index file
-     * @param path absolute pathname on filesystem
-     */
-    public QIndexFile(String path) throws IOException {
-        this.path = path;
-        fileObj = new File(path);
-
-        // if file doesn't exist, ensure parent dir exists, so subsequent
-        // file creation will (hopefully) succeed
-        if (!fileObj.exists())
-        {
-            // create parent directory if not already existing
-            String parentDir = fileObj.getParent();
-            File parentFile = new File(parentDir);
-            if (!parentFile.isDirectory())
-            {
-                parentFile.mkdirs();
-            }
-        }
-        
-        // get a random access object, creating file if not yet existing
-        file = new RandomAccessFile(fileObj, "rws");
-
-        // barf if file's length is not a multiple of record length
-        rawLength = file.length();
-        if (rawLength % recordLen != 0) {
-            throw new IOException("File size not a multiple of record length ("+recordLen+")");
-        }
-        
-        // note record count
-        numRecs = (int)(rawLength / recordLen);
-    }
-
-    /**
-     * fetch an iterator for items after a given time
-     */
-    public synchronized Iterator getItemsSince(int time) throws IOException
-    {
-        //System.out.println("getItemsSince: time="+time);
-
-        // if no records, return an empty iterator
-        if (numRecs == 0)
-        {
-            return new QIndexFileIterator(this, 0);
-        }
-        
-        // otherwise, binary search till we find an item time-stamped
-        // after given time
-        long mtime = ((long)time) * 1000;
-        int lo = 0;
-        int hi = numRecs;
-        int lastguess = -1;
-        while (hi - lo > 0)
-        {
-            int guess = (hi + lo) / 2;
-            //System.out.println("getItemsSince: lo="+lo+" guess="+guess+" hi="+hi);
-            if (guess == lastguess) // && hi - lo == 1)
-            {
-                break;
-            }
-            lastguess = guess;
-
-            Object [] rec = getRecord(guess);
-            long t = ((Long)rec[0]).longValue();
-            if (t <= mtime)
-            {
-                // guess too low, go for upper range
-                lo = guess;
-                continue;
-            }
-            else
-            {
-                // guess too high, pick lower range
-                hi = guess;
-                continue;
-            }
-        }
-
-        // found
-        return new QIndexFileIterator(this, hi);
-    }
-
-    /**
-     * adds a new base64 hash value record, saving it with current time
-     */
-    public synchronized void add(String h) throws IOException
-    {
-        // barf if hash is incorrect length
-        if (h.length() != hashLen)
-        {
-            System.out.println("hash="+h);
-            throw new IOException("Incorrect hash length ("+h.length()+"), should be "+hashLen);
-        }
-        
-        // format current date/time as decimal string, pad with leading zeroes
-        Date d = new Date();
-        String ds = String.valueOf(d.getTime());
-        while (ds.length() < timeLen)
-        {
-            ds = "0" + ds;
-        }
-
-        // now can construct record
-        String rec = ds + "," + h + "\n";
-
-        // append it to file
-        file.seek(numRecs * recordLen);
-        file.writeBytes(rec);
-        
-        // and update count
-        numRecs += 1;
-        rawLength += recordLen;
-    }
-
-    public long getRecordTime(int n) throws IOException
-    {
-        Object [] rec = getRecord(n);
-        
-        return ((Long)rec[0]).longValue();
-    }
-
-    /** return number of records currently within file */
-    public int length()
-    {
-        return numRecs;
-    }
-
-    /**
-     * returns the hash field of record n
-     */
-    public String getRecordHash(int n) throws IOException
-    {
-        Object [] rec = getRecord(n);
-        return (String)rec[1];
-    }
-
-    public synchronized Object [] getRecord(int n) throws IOException
-    {
-        Object [] rec = new Object[2];
-
-        String recStr = getRecordStr(n);
-        String [] flds = recStr.split(",");
-        Long t = new Long(flds[0]);
-        String h = flds[1];
-        rec[0] = t;
-        rec[1] = h;
-        return rec;
-    }
-    
-    protected synchronized String getRecordStr(int n) throws IOException
-    {
-        // barf if over or under-reaching
-        if (n < 0 || n > numRecs - 1)
-        {
-            throw new IOException("Record number ("+n+") out of range");
-        }
-
-        // position to location of the record
-        file.seek(n * recordLen);
-
-        // read, trim and return
-        return file.readLine().trim();
-    }
-
-    /**
-     * @param args the command line arguments
-     */
-    public static void main(String[] args) {
-        try {
-            QIndexFile q = new QIndexFile("/home/david/.quartermaster_client/content/index.dat");
-            Iterator i = q.getItemsSince((int)(new Date().getTime() / 1000));
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-}
diff --git a/apps/q/java/src/net/i2p/aum/q/QIndexFileIterator.java b/apps/q/java/src/net/i2p/aum/q/QIndexFileIterator.java
deleted file mode 100644
index 95a7df8457dda63624328b42af8d7bd34ea314f8..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QIndexFileIterator.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * QIndexFileIterator.java
- *
- * Created on March 24, 2005, 1:49 PM
- */
-
-package net.i2p.aum.q;
-
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-/**
- * Implements an Iterator for index files
- */
-public class QIndexFileIterator implements Iterator
-{
-    public QIndexFile file;
-    int recNum;
-
-    /** Creates an iterator starting from beginning of index file */
-    public QIndexFileIterator(QIndexFile qif)
-    {
-        this(qif, 0);
-    }
-    
-    /** Creates a new instance of QIndexFileIterator */
-    public QIndexFileIterator(QIndexFile qif, int recNum)
-    {
-        file = qif;
-        this.recNum = recNum;
-    }
-    
-    public boolean hasNext()
-    {
-        return recNum < file.length();
-    }
-    
-    public Object next() throws NoSuchElementException
-    {
-        String rec;
-        try {
-            rec = file.getRecordHash(recNum);
-        }
-        catch (Exception e) {
-            throw new NoSuchElementException("Reached end of index");
-        }
-        recNum += 1;
-        return rec;
-    }
-    
-    public void remove()
-    {
-    }
-    
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/q/QKademliaComparator.java b/apps/q/java/src/net/i2p/aum/q/QKademliaComparator.java
deleted file mode 100644
index 2b949b08dfd41bf563db4188a3b45b9401f90693..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QKademliaComparator.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * QKademliaComparator.java
- *
- * Created on March 30, 2005, 12:30 PM
- */
-
-package net.i2p.aum.q;
-
-import java.math.BigInteger;
-import java.util.Comparator;
-
-/**
- * implements a Comparator class which compares two QPeerRec objects
- * for kademlia-closeness to a given base64 sha hash value
- */
-public class QKademliaComparator implements Comparator {
-    
-    QNode node;
-    BigInteger hashed;
-    
-    /**
-     * Creates a kademlia comparator, which given a base64 sha256 hash
-     * of something, can compare two nodes for their kademlia-closeness to
-     * that hash
-     * @param node a QNode object - needed for access to its base64 routines
-     * @param base64hash - string - a base64 representation of the sha256 hash
-     * of anything
-     */
-    public QKademliaComparator(QNode node, String base64hash) {
-    
-        this.node = node;
-        hashed = new BigInteger(node.base64Dec(base64hash).getBytes());
-    }
-    
-    /**
-     * compares two given QPeerRec objects for how close each one's ID
-     * is to the stored hash
-     */
-    public int compare(Object o1, Object o2) {
-
-        QPeer peer1 = (QPeer)o1;
-        QPeer peer2 = (QPeer)o2;
-
-        String id1 = peer1.getId();
-        String id2 = peer2.getId();
-        
-        BigInteger i1 = new BigInteger(id1.getBytes());
-        BigInteger i2 = new BigInteger(id2.getBytes());
-
-        BigInteger xor1 = i1.xor(hashed);
-        BigInteger xor2 = i2.xor(hashed);
-
-        return xor1.compareTo(xor2);
-    }
-    
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/q/QMgr.java b/apps/q/java/src/net/i2p/aum/q/QMgr.java
deleted file mode 100644
index a9e048a6417dc15ef58a9055c292bb49bebe5f3c..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QMgr.java
+++ /dev/null
@@ -1,927 +0,0 @@
-/*
- * QLaunch.java
- *
- * Created on March 30, 2005, 10:09 PM
- */
-
-package net.i2p.aum.q;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Properties;
-import java.util.Vector;
-
-import net.i2p.aum.I2PXmlRpcClientFactory;
-import net.i2p.aum.PropertiesFile;
-import net.i2p.aum.SimpleFile;
-import net.i2p.data.Destination;
-
-import org.apache.xmlrpc.XmlRpcClient;
-
-/**
- * <p>Command Line Interface (CLI) for starting/stopping Q nodes,
- * and also, executing commands on Q nodes such as inserting, retrieving
- * and searching for content.</p>
- *
- * <p>Commands include:
- *   <ul>
- *     <li>Start a server or client Node</li>
- *     <li>Stop a server or client Node</li>
- *     <li>Get status of a server or client Node</li>
- *     <li>Export a server node's dest</li>
- *     <li>Import a foreign dest to a server or client node</li>
- *     <li>Insert a file to a client node, with metadata</li>
- *     <li>Retrieve data/metadata from a client node</li>
- *     <li>Search a client node for content</li>
- */
-public class QMgr {
-
-    public Runtime runtime;
-    public XmlRpcClient node;
-    public String nodePrivKey;
-    public String nodeDest;
-    public String nodeDirStr;
-    public File nodeDir;
-    public boolean isServer = false;
-
-    public String [] args;
-    public String cmd;
-    public int cmdIdx;
-    public int argc;
-    public int argi;
-    
-    public static String [] commonI2PSystemPropertyKeys = {
-        "i2cp.tcp.host",
-        "i2cp.tcp.port",
-        "eepproxy.tcp.host",
-        "eepproxy.tcp.port",
-        "q.xmlrpc.tcp.host",
-        "q.xmlrpc.tcp.port",
-        "inbound.length",
-        "outbound.length",
-        "inbound.lengthVariance",
-        "outbound.lengthVariance",
-    };
-
-    /** Creates a new instance of QLaunch */
-    public QMgr() {
-    }
-
-    public void notimplemented() {
-        usage(1, "Command '"+cmd+"' not yet implemented, sorry");
-    }
-
-    /** procures an XML-RPC client for node interaction */
-    public void getXmlRpcClient() {
-    
-        
-    }
-
-    public int doHelp() {
-        if (argi == argc) {
-            // output short help
-            System.out.println(
-                "I2P QMgr - Brief command summary:\n"
-                +"Synopsis:"
-                +"  java net.i2p.aum.q.QMgr [-dir <path>] [server] [<cmd> [<args>]]\n"
-                +"Commands:\n"
-                +"  help             - print this help summary\n"
-                +"  help verbose     - print detailed verbose usage info\n"
-                +"  start            - start a node in background\n"
-                +"  foreground       - run a node in foreground\n"
-                +"  stop             - terminate node\n"
-                +"  status           - display node status\n"
-                +"  getref [<file>]  - output the node's noderef (its base64 dest)\n"
-                +"  addref [<file>]  - add one or more node refs to node\n"
-                +"  get key [<file>] - get key to stdout (or to file)\n"
-                +"  put [<file>] [-m <metadata>] - insert content\n"
-                +"  search item1=val1 item2=val2...  - search for content\n"
-                );
-        }
-        else if (args[argi].equals("verbose")) {
-            System.out.println(
-                "----------------------------\n"
-                +"Welcome to the I2P Q network\n"
-                +"----------------------------\n"
-                +"\n"
-                +"This program, QMgr, is a command-line interface to the Q network,\n"
-                +"(an in-I2P distributed file store)\n"
-                +"and allows you to perform basic operations, including:\n"
-                +"\n"
-                +" - create, startup and shutdown Q server and client nodes\n"
-                +" - determine status of running Q nodes\n"
-                +" - import and export noderefs to/from these nodes\n"
-                +" - search for, insert and retrieve content\n"
-                +"\n"
-                +"Command syntax:\n"
-                +"  java net.i2p.aum.q.QMgr [-dir <path>] [-port <port>] [server] [<cmd> [<args>]]\n"
-                +"\n"
-                +"Explanation of commands and arguments:"
-                +"\n"
-                +"* 'server'\n"
-                +"  Specifies that we're operating on a server node (otherwise it's\n"
-                +"  assumed we're operating on a client node)\n"
-                +"\n"
-                +"* '-dir=<path>'\n"
-                +"  Server nodes by default reside at ~/.quartermaster_server,\n"
-                +"  and client nodes at ~/.quartermaster_client.\n"
-                +"  Nodes are uniquely identified by the directory at which they\n"
-                +"  reside. Specifying this argument allows you to operate on a\n"
-                +"  server or client node which resides at a different location\n"
-                +"\n"
-                +"* '-port=<port>'\n"
-                +"  Applies to client nodes only. Valid only for startup command.\n"
-                +"  Permanently changes the port on which a given client listens\n"
-                +"  for cmmands.\n"
-                +"\n"
-                +"* Commands - the basic commands are:\n"
-                +"\n"
-                +"  help\n"
-                +"    - display a help summary\n"
-                +"\n"
-                +"  help verbose\n"
-                +"    - display this long-winded help\n"
-                +"\n"
-                +"  start\n"
-                +"     - start the node. If a nonexistent directory path is given,\n"
-                +"       a whole new unique server or client node will be created\n"
-                +"       at that path\n"
-                +"\n"
-                +"  foreground\n"
-                +"     - as for start, but run the server in foreground rather\n"
-                +"       than as a background daemon\n"
-                +"\n"
-                +"  stop\n"
-                +"     - shutdown the node\n"
-                +"\n"
-                +"  status\n"
-                +"     - print a dump of node status and statistics to stdout\n"
-                +"\n"
-                +"  newkeys\n"
-                +"     - generate and print out a new keypair for signed-space\n"
-                +"       data item inserts\n"
-                +"\n"
-                +"  getref [<file>]\n"
-                +"     - print the node's noderef (its base64 destination) to\n"
-                +"       stdout. If <file> arg is given, writes the destination\n"
-                +"       to this file instead.\n"
-                +"\n"
-                +"  addref [<file>]\n"
-                +"     - add one or more noderefs to the node. If [<file>] argument\n"
-                +"       is given, the refs are read from this file, which is expected\n"
-                +"       to contain one base64 destination per line\n"
-                +"\n"
-                +"The following commands are only valid for client nodes:\n"
-                +"\n"
-                +"  get <key> [<file>]\n"
-                +"     - Try to retrieve a content item, (identified by <key>), from the\n"
-                +"       node. If the item is retrieved, its raw data will be printed\n"
-                +"       to stdout, or to <file> if given. NOTE - REDIRECTING TO STDOUT\n"
-                +"       IS PRESENTLY UNRELIABLE, SO SPECIFY AN EXPLICIT FILENAME FOR NOW\n"
-                +"\n"
-                +"  put [<file>] [-m item=val ...]\n"
-                +"     - Inserts an item of content to the node, and prints its key to\n"
-                +"       stdout. Reads content data from <file> if given, or from standard\n"
-                +"       input if not. Metadata arguments may be given as '-m' followed by\n"
-                +"       a space-separated sequence of 'item=value' specifiers.\n"
-                +"       Typical metadata items include:\n"
-                +"         - type (one of text/html/image/audio/video/archive)\n"
-                +"         - title - a short (<80 char) descriptive title\n"
-                +"         - filename - a recommended filename under which to store this\n"
-                +"           item on retrieve.\n"
-                +"         - abstract - a longer (<256 char) summary of content\n"
-                +"         - keywords - a comma-separated list of keywords\n"
-                +"\n"
-                +"  search -m item=val [ item=val ...]\n"
-                +"     - searches node for content matching a set of metadata criteria\n"
-                +"       each 'item=val' specifies an 'item' of metadata, to be matched\n"
-                +"       against regular expression 'val'. For example:\n"
-                +"         java net.i2p.aum.q.QMgr search -m title=\"^Madonna\" type=\"music\"\n"
-                );
-        }
-        else {
-            System.out.println(
-                "Unrecognised help qualifier '"+args[argi]+"'\n"
-                +"type 'java net.i2p.aum.q.QMgr help' for more info"
-                );
-        }
-        return 0;
-    }
-
-    public int doStart() {
-        //notimplemented();
-
-        String [] startForegroundArgs;
-        int i;
-
-        // Detect/add any '-D' settings
-        // search our list of known i2p-relevant sysprops, detect
-        // if they've been set in System properties, and if so, copy
-        // them to a customProps table
-        Hashtable customProps = new Hashtable();
-        Properties sysprops = System.getProperties();
-        for (i=0; i<commonI2PSystemPropertyKeys.length; i++) {
-            String key = commonI2PSystemPropertyKeys[i];
-            if (sysprops.containsKey(key)) {
-                customProps.put(key, sysprops.get(key));
-            }
-        }
-
-        // Create args list
-        args[cmdIdx] = "foreground";  // kludge = substitute 'foreground' command
-
-        // need original args plus 2 ('java' and 'classname') plus
-        // number of custom properties
-        startForegroundArgs = new String[argc+2+customProps.size()];
-
-        // create a set of startup args for child vm
-        startForegroundArgs[0] = "java";
-        i = 1;
-        Enumeration keys = customProps.keys();
-        while (keys.hasMoreElements()) {
-            String key = (String)keys.nextElement();
-            String val = (String)customProps.get(key);
-            startForegroundArgs[i++] = "-D"+key+"="+val;
-        }
-        startForegroundArgs[i++] = "net.i2p.aum.q.QMgr";
-        for (int j = 0; j < args.length; j++) {
-            startForegroundArgs[i+j] = args[j];
-        }
-
-        // and spawn the start job
-        try {
-            //runtime.exec(startForegroundArgs, propLines);
-            runtime.exec(startForegroundArgs, null);
-            for (i=0; i<startForegroundArgs.length; i++) {
-                System.out.println("start: arg["+i+"]="+startForegroundArgs[i]);
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-            return 1;
-        }
-        return 0;
-    }
-
-    /** this gets invoked after a 'start' command, and runs in a detached process */
-    public int doStartForeground() {
-
-        //new File("/tmp/blahblah").mkdirs();
-        /**
-        String s = new String();
-        int i = 0;
-        for (i=0; i<args.length; i++) {
-            s = s + args[i] + "\n";
-        }
-        try {
-            new SimpleFile("/tmp/qq", "rws").write(s);
-        } catch (IOException e) {}
-        //notimplemented();
-        **/
-
-        QNode node;
-
-        try {
-            if (isServer) {
-                node = new QServerNode(nodeDirStr);
-            }
-            else {
-                node = new QClientNode(nodeDirStr);
-            }
-
-            node.run();
-            
-            // enter endless loop
-            //while (true) {
-            //    Thread.sleep(1000000000);
-            //}
-
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-        return 0;
-    }
-
-    public int doStop() {
-
-        System.out.println("Stopping node at '"+nodeDirStr+"'...");
-        Vector nodeArgs = new Vector();
-        nodeArgs.addElement(nodePrivKey);
-        try {
-            Hashtable res = (Hashtable)node.execute("i2p.q.shutdown", nodeArgs);
-            System.out.println("Shutdown failed: got "+res);
-            return 1;
-        } catch (IOException e) {
-            System.out.println("Shutdown apparently ok");
-        } catch (Exception e) {
-            e.printStackTrace();
-            System.out.println("Problem with shutdown");
-            return 1;
-        }
-
-        return 0;
-    }
-
-    public int doStatus() {
-        System.out.println("Pinging node at '"+nodeDirStr+"'...");
-        try {
-            Hashtable res = (Hashtable)node.execute("i2p.q.ping", new Vector());
-            System.out.println("Node Ping:");
-            Enumeration eres = res.keys();
-            while (eres.hasMoreElements()) {
-                String key = (String)eres.nextElement();
-                Object val = res.get(key);
-                System.out.println("  "+key+"="+val);
-            }
-
-        } catch (Exception e) {
-            e.printStackTrace();
-            System.out.println("Failed to ping node");
-            return 1;
-        }
-
-        return 0;
-    }
-
-    /** executes a 'getref' command */
-    public int doGetRef() {
-        if (!isServer) {
-            System.err.println("Cannot get noderefs for client nodes");
-            return 1;
-        }
-        if (argi < argc) {
-            // write it to a file
-            String path = args[argi];
-            try {
-                new SimpleFile(path, "rws").write(nodeDest);
-            } catch (IOException e) {
-                e.printStackTrace();
-                usage("getref: Cannot create/write output file "+path);
-            }
-        }
-        else {
-            System.out.println(nodeDest);
-        }
-        return 0;
-    }
-
-    /**
-     * attempts to add a single dest
-     * @return true if dest added successfully, false if not
-     */
-    public boolean doAddOneRef(String ref) {
-        
-        Destination d;
-        Hashtable res;
-
-        // don't trust user or xmlrpc link, try to create a dest
-        // from purported string now
-        try {
-            d = new Destination();
-            d.fromBase64(ref);
-        } catch (Exception e) {
-            System.out.println("Invalid destination: '"+ref+"'");
-            return false;
-        }
-        
-        // looks ok, try to pass it to node
-        try {
-            Vector v = new Vector();
-            v.addElement(ref);
-            res = (Hashtable)node.execute("i2p.q.hello", v);
-        } catch (Exception e) {
-            e.printStackTrace();
-            System.out.println("Failed to add noderef '"+ref+"'");
-            return false;
-        }
-        
-        System.out.println("doAddRef: res="+res);
-
-        // see what result we got
-        String status = (String)res.get("status");
-
-        System.out.println("doAddRef: status="+status);
-
-        if (status == null || !status.equals("ok")) {
-            String error = (String)res.get("error");
-            System.out.println("Error '"+error+"' trying to add node dest '"+ref+"'");
-            return false;
-        }
-        
-        // success
-        return true;
-    }
-
-    /** executes an 'addref' command */
-    public int doAddRef() {
-        
-        if (argi < argc) {
-            // open file, split into lines, submit each line as dest
-            String path = args[argi];
-            String raw;
-            try {
-                raw = new SimpleFile(path, "r").read().trim();
-            } catch (IOException e) {
-                e.printStackTrace();
-                System.out.println("addref: failed to open file '"+path+"'");
-                return 1;
-            }
-            String [] lines = raw.split("\\s+");
-            //int i;
-            for (int i=0; i<lines.length; i++) {
-
-                String line = lines[i];
-
-                // ignore empty and comment lines
-                if (line.substring(0,1).equals("#")) {
-                    continue;
-                }
-
-                if (!doAddOneRef(line)) {
-                    // failed
-                    return 1;
-                }
-            }
-        }
-        else {
-            // read lines from stdin
-            InputStreamReader rIn = new InputStreamReader(System.in);
-            BufferedReader brIn = new BufferedReader(rIn);
-
-            String line;
-            
-            while (true) {
-                try {
-                    line = brIn.readLine();
-                } catch (IOException e) {
-                    e.printStackTrace();
-                    return 1;
-                }
-                if (line == null) {
-                    break;
-                }
-                
-                if (!doAddOneRef(line)) {
-                    // failed
-                    return 1;
-                }
-            }
-            
-        }
-        
-        return 0;
-    }
-
-    public int doGet() {
-
-        if (argi == argc) {
-            usage("get: missing key");
-        }
-        
-        String key = args[argi++];
-
-        System.err.println("Trying to retrieve key '"+key+"'...");
-
-        Hashtable res;
-        try {
-            Vector getArgs = new Vector();
-            getArgs.addElement(key);
-            res = (Hashtable)node.execute("i2p.q.getItem", getArgs);
-            //System.err.println("get: res="+res);
-
-        } catch (Exception e) {
-            e.printStackTrace();
-            System.out.println("Failed to ping node");
-            return 1;
-        }
-        
-        String status = (String)res.get("status");
-        if (!status.equals("ok")) {
-            String err = (String)res.get("error");
-            System.err.println("Key retrieve error: "+err);
-            String comment = (String)res.get("comment");
-            if (comment != null) {
-                System.err.println("  "+comment);
-            }
-            return 1;
-        }
-
-        // got something
-        byte [] data = (byte [])res.get("data");
-
-        // save to file, or spit out to stdout?
-        if (argi < argc) {
-            // spit to file
-            String path = args[argi];
-            try {
-                new SimpleFile(path, "rws").write(data);
-            } catch (Exception e) {
-                e.printStackTrace(System.err);
-                System.err.println("Failed to save data to file '"+path);
-                return 1;
-            }
-        }
-        else {
-            // dump to stdout
-            //System.out.print(data);
-            for (int i=0; i<data.length; i++) {
-                char c = (char)data[i];
-                System.out.print(c);
-            }
-            System.out.flush();
-        }
-        return 0;
-    }
-
-    public int doPut() {
-
-        Hashtable metadata = new Hashtable();
-        String path = null;
-
-        // different ways of sourcing data/metadata
-        if (argi < argc) {
-            // provided filename and/or metadata
-            if (!args[argi].equals("-m")) {
-                // read from file
-                path = args[argi++];
-            }
-            
-            // now expect -m, or error
-            if (argi >= argc || !args[argi].equals("-m")) {
-                usage("Bad put command syntax");
-            }
-
-            // now skip over the '-m'
-            argi++;
-
-            metadata = readMetadataSpec();
-        }
-
-        byte [] data = null;
-
-        if (path != null) {
-            // easy way - suck the file or barf
-            try {
-                data = new SimpleFile(path, "r").readBytes();
-            } catch (IOException e) {
-                e.printStackTrace();
-                usage("get: Failed to read input file '"+path+"'");
-            }
-        }
-        else {
-            // the crap option - suck it from stdin
-            // read lines from stdin
-            ByteArrayOutputStream bo = new ByteArrayOutputStream();
-            int c;
-            try {
-                while (true) {
-                    c = System.in.read();
-                    if (c < 0) {
-                        break;
-                    }
-                    bo.write(c);
-                }
-            } catch (Exception e) {
-                e.printStackTrace();
-                usage("put: error reading from input stream");
-            }
-            
-            data = bo.toByteArray();
-        }
-
-        // ok, got data (and possibly metadata too)
-        Vector putArgs = new Vector();
-        Hashtable res;
-        putArgs.addElement(metadata);
-        putArgs.addElement(data);
-        
-        System.out.println("data length="+data.length);
-
-        try {
-            res = (Hashtable)node.execute("i2p.q.putItem", putArgs);
-        } catch (Exception e) {
-            e.printStackTrace(System.err);
-            System.err.println("Failed to put");
-            return 1;
-        }
-        
-        // got a res
-        String status = (String)res.get("status");
-        if (!status.equals("ok")) {
-            String error = (String)res.get("error");
-            usage("put: failure - "+error);
-        }
-
-        // success
-        String key = (String)res.get("key");
-        System.out.print(key);
-        System.out.flush();
-        
-        return 0;
-    }
-
-    public int doNewKeys() {
-        
-        System.err.println("Generating new signed-space keypair...");
-
-        String [] keys = QUtil.newKeys();
-        System.out.println("Public: "+keys[0]);
-        System.out.println("Private: "+keys[1]);
-        
-        return 0;
-    }
-
-    public int doSearch() {
-
-        if (argi == argc) {
-            usage("Missing search metadata");
-        }
-
-        // expect -m, or error
-        if (argi >= argc || !args[argi].equals("-m")) {
-            usage("Bad search command syntax");
-        }
-
-        // now skip over the '-m'
-        argi++;
-
-        if (argi == argc) {
-            usage("Missing search metadata");
-        }
-
-        Hashtable metadata = readMetadataSpec();
-
-        // ok, got data (and possibly metadata too)
-        Vector searchArgs = new Vector();
-        Hashtable res;
-        searchArgs.addElement(metadata);
-        try {
-            res = (Hashtable)node.execute("i2p.q.search", searchArgs);
-        } catch (Exception e) {
-            e.printStackTrace(System.err);
-            System.err.println("Failed to search");
-            return 1;
-        }
-        
-        // got a res
-        String status = (String)res.get("status");
-        if (!status.equals("ok")) {
-            String error = (String)res.get("error");
-            usage("search: failure - "+error);
-        }
-
-        // success
-        Vector items = (Vector)res.get("items");
-        
-        //System.out.println(items);
-
-        for (int i=0; i<items.size(); i++) {
-            Hashtable rec = (Hashtable)items.get(i);
-            String key = (String)rec.get("key");
-            if (key == null) {
-                continue;
-            }
-            System.out.println(key);
-            Enumeration keys = rec.keys();
-            while (keys.hasMoreElements()) {
-                Object mkey = keys.nextElement();
-                if (!mkey.toString().equals("key")) {
-                    Object val = rec.get(mkey);
-                    System.out.println("  "+mkey+"="+val);
-                }
-            }
-            
-        }
-        
-        return 0;
-    }
-
-    public Hashtable readMetadataSpec() {
-        Hashtable meta = new Hashtable();
-
-        //dumpArgs();
-
-        // rest of args are metadata
-        while (argi < argc) {
-            String metaArg = args[argi++];
-            String [] parts = metaArg.split("=", 2);
-            // barf if just 1 part
-            if (parts.length < 2) {
-                usage("Illegal metadata arg '"+metaArg+"'");
-            }
-
-            // add to search map
-            meta.put(parts[0], parts[1]);
-        }
-        
-        return meta;
-    }
-
-    public void dumpArgs() {
-        System.out.println("Dump of QMgr shell args:");
-        for (int i=0; i<args.length; i++) {
-            System.out.println("args["+i+"]='"+args[i]+"'");
-        }
-    }
-
-    public int execute(String [] args) {
-
-        argi = 0;
-        argc = args.length;
-        this.args = args;
-        
-        runtime = Runtime.getRuntime();
-
-        // barf if no cmds given
-        if (argc == 0) {
-            usage("Missing command");
-        }
-
-        // test if specifying a directory
-        if (args[argi].equals("-dir")) {
-            // barf if no dir arg
-            argi++;
-            if (argi == argc) {
-                usage("-dir: missing directory");
-            }
-            nodeDirStr = args[argi++];
-        }
-
-        // test if specifying a port
-        if (args[argi].equals("-port")) {
-            // barf if no port arg
-            argi++;
-            if (argi == argc) {
-                usage("-port: missing port num");
-            }
-            System.setProperty("q.xmlrpc.tcp.port", args[argi++]);
-        }
-
-        // test if server
-        if (argi < argc && args[argi].equals("server")) {
-            isServer = true;
-            argi++;
-        }
-        
-        // barf if no more arg
-        if (argi == argc){
-            usage("Missing command");
-        }
-
-        // cool, got at least a keyword
-        cmdIdx = argi++;
-        cmd = args[cmdIdx];
-        
-        // and dispatch off to appropriate handler
-        if (cmd.equals("help")) {
-            return doHelp();
-        }
-
-        // following commands deal with a node, so gotta get a handle
-        if (nodeDirStr == null) {
-            // fall back on defaults
-            if (isServer) {
-                nodeDirStr = System.getProperties().getProperty("user.home")
-                    + QServerNode.sep
-                    + QServerNode.defaultStoreDir;
-            }
-            else {
-                nodeDirStr = System.getProperties().getProperty("user.home")
-                    + QClientNode.sep
-                    + QClientNode.defaultStoreDir;
-            }
-        }
-        nodeDir = new File(nodeDirStr);
-        
-        //System.out.println("nodeDirStr='"+nodeDirStr+"'");
-        
-        if (cmd.equals("start")) {
-            return doStart();
-        }
-        
-        else if (cmd.equals("foreground")) {
-            // secret option, used when starting node
-            return doStartForeground();
-        }
-
-        // the following commands require that the node actually exists
-        if (!nodeDir.isDirectory()) {
-            usage(
-                "Nonexistent node directory '"+nodeDirStr+"'\n"
-                +"The '"+cmd+"' command requires that the node already\n"
-                +"exists. You may use the 'start' command to create a\n"
-                +"whole new node at that directory if you wish"
-                );
-        }
-
-        // yay, found a node (we hope), create an xmlrpc client for talking
-        // to that node
-        String propPath = nodeDirStr + QNode.sep + "node.conf";
-        PropertiesFile pf;
-        try {
-            pf = new PropertiesFile(propPath);
-        } catch (IOException e) {
-            e.printStackTrace();
-            System.out.println("Failed to load node properties");
-            return -1;
-        }
-
-        nodePrivKey = pf.getProperty("privKey");
-        nodeDest = pf.getProperty("dest");
-       
-        if (isServer) {
-            // gotta create I2P xmlrpc client
-            try {
-                node = new I2PXmlRpcClientFactory().newClient(nodeDest);
-            } catch (Exception e) {
-                e.printStackTrace();
-                System.out.println("Failed to create I2P XML-RPC connection to node");
-                return -1;
-            }
-        }
-        else {
-            int xmlRpcServerPort = pf.getIntProperty(
-                "xmlRpcServerPort", QNode.defaultXmlRpcServerPort);
-            // create normal xmlrpc client
-            try {
-                node = new XmlRpcClient("http://localhost:"+xmlRpcServerPort);
-            } catch (Exception e) {
-                e.printStackTrace();
-                System.out.println("Failed to create I2P XML-RPC connection to node");
-                return -1;
-            }
-        }
-        
-        if (cmd.equals("stop")) {
-            return doStop();
-        }
-        else if (cmd.equals("status")) {
-            return doStatus();
-        }
-        else if (cmd.equals("getref")) {
-            return doGetRef();
-        }
-        else if (cmd.equals("addref")) {
-            return doAddRef();
-        }
-        else if (cmd.equals("get")) {
-            return doGet();
-        }
-        else if (cmd.equals("put")) {
-            return doPut();
-        }
-        else if (cmd.equals("newkeys")) {
-            return doNewKeys();
-        }
-        else if (cmd.equals("search")) {
-            return doSearch();
-        }
-        else {
-            usage("unrecognised command '"+cmd+"'");
-        }
-        
-        return 0; // needed to shut the stupid compiler up
-    }
-
-    // barf-o-matic methods
-
-    public int usage() {
-        return usage(1);
-    }
-
-    public int usage(String msg) {
-        return usage(1, msg);
-    }
-    
-    public int usage(int retval) {
-        return usage(retval, null);
-    }
-    
-    public int usage(int retval, String msg) {
-        System.out.println(msg);
-        System.out.println(
-            "Usage: java net.i2p.aum.q.QMgr [-dir=<path>] [server] [cmd [args]]\n"
-            +"Type 'java net.i2p.aum.q.QMgr help' for help summary\n"
-            +"or 'java net.i2p.aum.q.QMgr help verbose' for long-winded help"
-            );
-        System.exit(retval);
-        return 0; // stop silly compiler from whingeing
-    }
-
-    /**
-     * Startup a Q server or client node, or send a command to a running node
-     * @param args the command line arguments
-     */
-    public static void main(String[] args) {
-        QMgr mgr = new QMgr();
-        int retval = mgr.execute(args);
-        System.exit(retval);
-    }
-    
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/q/QNode.java b/apps/q/java/src/net/i2p/aum/q/QNode.java
deleted file mode 100644
index bb4a29c2a1bb5242a098e690b1a5e0bc9bda9134..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QNode.java
+++ /dev/null
@@ -1,1976 +0,0 @@
-/*
- * QNode.java
- *
- * Created on 20 March 2005, 23:27
- */
-
-package net.i2p.aum.q;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.RandomAccessFile;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Properties;
-import java.util.Vector;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-
-import net.i2p.I2PAppContext;
-import net.i2p.I2PException;
-import net.i2p.aum.EmbargoedQueue;
-import net.i2p.aum.I2PXmlRpcClient;
-import net.i2p.aum.I2PXmlRpcClientFactory;
-import net.i2p.aum.I2PXmlRpcServer;
-import net.i2p.aum.PrivDestination;
-import net.i2p.aum.PropertiesFile;
-import net.i2p.aum.SimpleFile;
-import net.i2p.aum.SimpleSemaphore;
-import net.i2p.client.I2PClient;
-import net.i2p.client.I2PClientFactory;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Destination;
-
-import org.apache.xmlrpc.XmlRpcException;
-
-//import gnu.crypto.hash.*;
-
-
-/**
- * Base class for Quartermaster nodes. Contains mechanisms for local datastore
- * and 
- * 
- */
-public abstract class QNode extends Thread
-{
-
-    /** get an i2p context */
-    public I2PAppContext i2p;
-    
-    // XML-RPC service name base
-    public static String baseXmlRpcServiceName = "i2p.q";
-
-    // generator of XML-RPC client objects
-    public I2PXmlRpcClientFactory peerInterfaceGen;
-
-    // directory requirements
-    public static String [] coreSubDirs = { "peers", "content", "locations", "catalog", "jobs"};
-    public static String [] extraSubDirs = {};
-
-    // thread pooling
-    public static int defaultMaxThreads = 3;
-    protected SimpleSemaphore threadPool;
-    protected EmbargoedQueue jobQueue;
-
-    // directory paths of this node
-
-    /** base path of our datastore directory */
-    public String dataDir;
-    
-    /** subdirectory of peers records */
-    public String peersDir;
-    
-    /** index file of peers */
-    public QIndexFile peersIdx;
-    
-    /** subdirectory of catalog records */
-    public String catalogDir;
-    
-    /** subdirectory of catalog location records */
-    public String locationDir;
-    
-    /** index file of peers */
-    public QIndexFile catalogIdx;
-    
-    /** subdirectory of content and metadata items */
-    public String contentDir;
-    
-    /** directory where resources live */
-    public String resourcesDir;
-
-    /** index file of peers */
-    public QIndexFile contentIdx;
-    
-    /** subdirectory of job records */
-    public String jobsDir;
-
-    /** index file of jobs */
-    public QIndexFile jobsIdx;
-
-    /** private key, as base64 string */
-    public String privKeyStr;
-    
-    /** public dest, as base64 string */
-    public String destStr;
-
-    /** our own node ID - SHA1(dest) */
-    public String id;
-
-    /** our own node private key */
-    public PrivDestination privKey;
-    
-    /** our own destination */
-    public Destination dest;
-
-    /** general node config properties */
-    public PropertiesFile conf;
-    
-    /** path of node's config file */
-    public String configPath;
-
-    /** convenience */
-    public static String sep = File.separator;
-
-    public I2PXmlRpcServer xmlRpcServer;
-
-    /** map of all known peers */
-    public Hashtable peers;
-
-    /**
-     * override in subclass
-     */
-    public static String defaultStoreDir = ".quartermaster";
-
-    // status attributes
-    /** time node got online */
-    public Date nodeStartTime;
-    
-    // logging file
-    public RandomAccessFile logFile;
-    public net.i2p.util.Log log;
-
-    public static int updateCatalogFromPeers = 0;
-    
-    public boolean isClient = false;
-
-    public double load_yPrev = 0.0;
-    public long load_tPrev = 0;
-    public double load_kRise = 10.0;
-    public double load_kFall = 800000.0;
-
-    public int load_backoffMin = 180;
-    public int load_backoffBits = 13;
-    public double load_backoffBase = 3.0;
-
-    // client only
-    public String xmlRpcServerHost = "";
-    public int xmlRpcServerPort = 7651;
-    public static int defaultXmlRpcServerPort = 7651;
-    
-    /** Number of pending content uploads. You should never shut down a
-     * node while this value is non-zero. You can get the current value
-     * of this via a node 'ping' command
-     */
-    public int numPendingUploads = 0;
-    
-    /** unixtime in millisecs of last incoming xml-rpc hit to this node, used
-     * in calculating node load
-     */
-
-    public String nodeType = "(base)";
-    
-    public boolean isRunning;
-
-    // ----------------------------------------------------------
-    // CONSTRUCTORS
-    // ----------------------------------------------------------
-
-    /** 
-     * Creates a new QNode instance, with store tree located
-     * at default location
-     */
-    public QNode() throws IOException, DataFormatException, I2PException
-    {
-        this(System.getProperties().getProperty("user.home") + sep + defaultStoreDir);
-        log.info("Constructor finished");
-    }
-
-    /**
-     * Creates a Q node, using specified datastore directory
-     * @param dataDir absolute pathname where this server's datastore tree is
-     * located. If tree doesn't exist, it will be created along with new keys
-     */
-    public QNode(String dataDir) throws IOException, DataFormatException, I2PException
-    {
-        // establish ourself as a thread
-        super();
-
-        setupStoreTree(dataDir);
-        getConfig();
-        peerInterfaceGen = new I2PXmlRpcClientFactory();
-
-        // determine threads limit
-        int maxThreads = defaultMaxThreads;
-        String maxThreadsStr = System.getProperty("qnode.maxthreads");
-        if (maxThreadsStr != null)
-        {
-            try {
-                maxThreads = Integer.getInteger(maxThreadsStr).intValue();
-            } catch (Exception e) {
-                e.printStackTrace();
-                log.error("Invalid qnode.maxThreads setting '"+maxThreadsStr+"'");
-            }
-        }
-        
-        // set up thread pool and job queue
-        threadPool = new SimpleSemaphore(maxThreads);
-        jobQueue = new EmbargoedQueue();
-
-        // load all known peers into peers table
-        loadPeers();
-
-        // for benefit of subclasses
-        //System.out.println("Invoking setup, isClient="+isClient);
-        setup();
-        System.out.println("after setup, isClient="+isClient);
-        
-        // queue up the first lot of jobs
-        scheduleStartupJobs();
-
-        // now launch our background
-        //log.info("launching background engine");
-        //start();
-
-    }
-
-    public void loadPeers() throws IOException
-    {
-        // populate job queue with jobs for all known servers
-        // man, doesn't it feel good to eat up memory by the gigabyte!! :P
-        Iterator peerIds = peersIdx.getItemsSince(0);
-        QPeer peerRec;
-        peers = new Hashtable();
-        while (peerIds.hasNext())
-        {
-            String peerId = (String)peerIds.next();
-            try {
-                peerRec = getPeerRecord(peerId);
-            } catch (Exception e) {
-                log.error("Failed to load peer '"+peerId+"'", e);
-                continue;
-            }
-            peers.put(peerId, peerRec);
-        }
-    }
-
-    // --------------------------------------------
-    // XML-RPC FRONT-END
-    // --------------------------------------------
-
-    /**
-     * <p>Sets up and launches an xml-rpc server for servicing requests
-     * to this node.</p>
-     * <p>For server nodes, the xml-rpc server listens within I2P on the
-     * node's destination.</p>
-     * <p>For client nodes, the xml-rpc server listens on a local TCP
-     * port (according to attributes xmlRpcServerHost and xmlRpcServerPort)</p>
-     */
-    public abstract void startExternalInterfaces(QServerMethods methods)
-            throws Exception;
-
-
-    // --------------------------------------------
-    // XML-RPC BACKEND
-    // --------------------------------------------
-
-    /**
-     * Dispatches a XML-RPC call to remote peer
-     */
-    public Hashtable peerExecute(String peerId, String name, Vector args) 
-        throws XmlRpcException, IOException, DataFormatException
-    {
-        // get peer record
-        QPeer peerRec = getPeerRecord(peerId);
-        
-        // need peer's dest
-        String dest64 = peerRec.destStr;
-
-        // need xmlrpc client obj
-        log.debug("peerExecute: name="+name+", id="+peerId+", dest="+dest64);
-
-        I2PXmlRpcClient client = peerInterfaceGen.newClient(dest64);
-        
-        // execute the request
-        Object result = client.execute(baseXmlRpcServiceName+"."+name, args);
-        
-        // ensure it's a hashtable
-        if (!result.getClass().isAssignableFrom(Hashtable.class)) {
-            throw new XmlRpcException(0, "Expected Hashtable in peer reply");
-        }
-
-        // all ok
-        return (Hashtable)result;
-    }
-
-    // --------------------------------------
-    // METHODS - initialisation
-    // --------------------------------------
-
-    /** perform mode-specific setup - overridden in subclasses */
-    public void setup() throws DataFormatException, I2PException
-    {
-    }
-
-    /**
-     * Checks the store directory tree, creating any missing
-     * directories
-     */
-    public void setupStoreTree(String dataDir) throws IOException
-    {
-        this.dataDir = dataDir;
-        int i;
-        File rootDir = new File(dataDir);
-
-        // ensure parent exists
-        if (!rootDir.isDirectory()) {
-            rootDir.mkdirs();
-        }
-        String logPath = dataDir + sep + "node.log";
-
-        // set up node-specific logger
-        Properties envProps = new Properties();
-        envProps.setProperty("loggerFilenameOverride", logPath);
-
-        //i2p = new I2PAppContext(envProps);
-        i2p = I2PAppContext.getGlobalContext();
-
-        log = i2p.logManager().getLog(this.getClass());
-        
-        //System.out.println("HASHTEST1: "+sha256Base64("hello, one, two three".getBytes()));
-        //System.out.println("BASE64TEST1: "+base64Enc("hello, one two three"));
-        //byte [] shit = {39,-20,54,-93,-19,-33,-61,65,-91,-85,
-        //            -19,25,-31,-81,20,-125,26,92,-51,-100,83,43,38,58,77,72,3,40,-78,-62,79,0,
-        //};
-        //System.out.println("BASE64TEST2: "+base64Enc(shit));
-
-        log.setMinimumPriority(log.DEBUG);
-        
-        log.info("creating server at directory "+dataDir);
-
-        /**
-        if (!logFileObj.isFile()) {
-            logFileObj.createNewFile();
-        }
-        System.out.println("Created logfile at "+logPath);
-        logFile = new RandomAccessFile(logFileObj, "rws");
-        */
-
-        // create core subdirectories
-        for (i = 0; i < coreSubDirs.length; i++)
-        {
-            String subdir = dataDir + sep + coreSubDirs[i];
-            File d = new File(subdir);
-            if (!d.isDirectory())
-            {
-                log.info("Creating datastore subdirectory '"+subdir+"'");
-                if (!d.mkdirs())
-                {
-                    throw new IOException("Failed to create directory "+subdir);
-                }
-            }
-        }
-
-        // create supplementary subdirectories
-        for (i = 0; i < extraSubDirs.length; i++)
-        {
-            String subdir = dataDir + sep + extraSubDirs[i];
-            File d = new File(subdir);
-            if (!d.isDirectory())
-            {
-                log.info("Creating supplementary datastore subdir '"+subdir+"'");
-                if (!d.mkdirs())
-                {
-                    throw new IOException("Failed to create directory "+subdir);
-                }
-            }
-        }
-
-        // store pathnames of core subdirectories
-        peersDir = dataDir + sep + "peers";
-        peersIdx = new QIndexFile(peersDir + sep + "index.dat");
-
-        catalogDir = dataDir + sep + "catalog";
-        catalogIdx = new QIndexFile(catalogDir + sep + "index.dat");
-        locationDir = dataDir + sep + "locations";
-        
-        contentDir = dataDir + sep + "content";
-        contentIdx = new QIndexFile(contentDir + sep + "index.dat");
-        
-        jobsDir = dataDir + sep + "jobs";
-        jobsIdx = new QIndexFile(jobsDir + sep + "index.dat");
-
-        // extract resources directory from jarfile (or wherever)
-        getResources();
-
-    }
-
-    public void getConfig() throws IOException, DataFormatException, I2PException
-    {
-        // create a config object, and stick in any missing defaults
-        String confPath = dataDir + sep + "node.conf";
-        conf = new PropertiesFile(confPath);
-
-        // generate a new dest, if one doesn't already exist
-        privKeyStr = conf.getProperty("privKey");
-        if (privKeyStr == null)
-        {
-            // need to generate whole new config
-            log.info("No private key found, generating new one");
-                
-            ByteArrayOutputStream privBytes = new ByteArrayOutputStream();
-            I2PClient client = I2PClientFactory.createClient();
-
-            // save attributes
-            dest = client.createDestination(privBytes);
-            privKey = new PrivDestination(privBytes.toByteArray());
-            privKeyStr = privKey.toBase64();
-            destStr = dest.toBase64();
-
-            // save out keys to files
-            String privKeyPath = dataDir + sep + "nodeKey.priv";
-            SimpleFile.write(privKeyPath, privKey.toBase64());
-            String destPath = dataDir + sep + "nodeKey.pub";
-            SimpleFile.write(destPath, dest.toBase64());
-
-            // now we can figure out our own node ID
-            id = destToId(dest);
-
-            // and populate our stored config
-            conf.setProperty("dest", dest.toBase64());
-            conf.setProperty("privKey", privKey.toBase64());
-            conf.setProperty("id", id);
-            conf.setProperty("numPeers", "0");
-            conf.setDoubleProperty("loadDampRise", load_kRise);
-            conf.setDoubleProperty("loadDampFall", load_kFall);
-            conf.setIntProperty("loadBackoffMin", load_backoffMin);
-            conf.setIntProperty("loadBackoffBits", load_backoffBits);
-
-            // these items only relevant to client nodes
-            conf.setIntProperty("xmlRpcServerPort", xmlRpcServerPort);
-            
-            log.info("Saved new keys, and nodeID " + id);
-        }
-        else
-        {
-            // already got a config, load it
-            //System.out.println("loading config");
-            dest = new Destination();
-            dest.fromBase64(conf.getProperty("dest"));
-            destStr = dest.toBase64();
-            privKey = PrivDestination.fromBase64String(conf.getProperty("privKey"));
-            privKeyStr = privKey.toBase64();
-            id = conf.getProperty("id");
-            load_kRise = conf.getDoubleProperty("loadDampRise", load_kRise);
-            load_kFall = conf.getDoubleProperty("loadDampFall", load_kFall);
-            load_backoffMin = conf.getIntProperty("loadBackoffMin", load_backoffMin);
-            load_backoffBits = conf.getIntProperty("loadBackoffBits", load_backoffBits);
-
-            // these items only relevant to client nodes
-            xmlRpcServerPort = conf.getIntProperty("xmlRpcServerPort", xmlRpcServerPort);
-
-            //System.out.println("our privkey="+privKeyStr);
-            if (privKeyStr == null) {
-                privKeyStr = conf.getProperty("privKey");
-                //System.out.println("our privkey="+privKeyStr);
-            }
-        }
-    }
-
-    /**
-     * Copies resources from jarfile (or wherever) into datastore dir.
-     * Somwhat of a kludge which determines if the needed resources
-     * reside within a jarfile or on the host filesystem.
-     * If the resources live in a jarfile, we extract them and
-     * copy them into the 'resources' subdirectory of our datastore
-     * directory. If they live in a directory on the host filesystem,
-     * we configure the node to access the resources directly from that
-     * directory instead.
-     */
-    public void getResources() throws IOException {
-
-        String resPath = dataDir + sep + "resources";
-        File resDir = new File(resPath);
-        ClassLoader cl = this.getClass().getClassLoader();
-        String jarPath = cl.getResource("qresources").getPath();
-        System.out.println("jarPath='"+jarPath+"'");
-        if (jarPath.startsWith("jar:")) {
-            jarPath = jarPath.split("jar:")[1];
-        }
-        
-        if (jarPath.startsWith("file:")) {
-            jarPath = jarPath.split("file:")[1];
-        }
-        int bangIdx = jarPath.indexOf("!");
-        //System.out.println("jarPath='"+jarPath+"' bangIdx="+bangIdx);
-        if (bangIdx > 0) {
-            jarPath = jarPath.substring(0, bangIdx);
-        }
-        
-        if (!jarPath.endsWith(".jar")) {
-            
-            // easy - found a directory with our resources
-            resourcesDir = jarPath;
-            System.out.println("Found physical resources dir: '"+resourcesDir+"'");
-            return;
-        }
-        System.out.println("jarPath='"+jarPath+"'");
-
-        // harder case - create resources dir, copy across resources
-        if (!resDir.isDirectory()) {
-            resDir.mkdirs();
-        }
-        resourcesDir = resDir.getPath();
-        
-        JarFile jf = new JarFile(jarPath);
-        Enumeration jfe = jf.entries();
-        Vector entlist = new Vector();
-        while (jfe.hasMoreElements()) {
-            JarEntry ent = (JarEntry)jfe.nextElement();
-            String name = ent.getName();
-            if (name.startsWith("qresources") && !ent.isDirectory()) {
-                entlist.addElement(name);
-                System.out.println("Need to extract resource: "+name);
-                String absPath = resDir.getPath() + sep + name.split("qresources/")[1];
-                File absFile = new File(absPath);
-                File parent = absFile.getParentFile();
-                if (!parent.isDirectory()) {
-                    parent.mkdirs();
-                }
-                // finally, can create and copy the file
-                FileWriter fw = new FileWriter(absFile);
-                InputStream is = cl.getResourceAsStream(name);
-                int c;
-                while ((c = is.read()) >= 0) {
-                    fw.write(c);
-                }
-                fw.close();
-            }
-        }
-    }
-
-    /**
-     * given a 'logical resource path', such as 'html/page.html',
-     * returns an absolute pathname on the host filesystem of
-     * the needed file
-     */
-    public String getResourcePath(String name) {
-        return resourcesDir + sep + name;
-    }
-
-    // --------------------------------------
-    // METHODS - scheduling and traffic control
-    //
-    // Background processing depends on node type:
-    //  - all nodes:
-    //     - peer list synchronisation
-    //  - client nodes
-    //     - catalog synchronisation
-    //     - content insertion, triggered by local
-    //       insertion
-    //  - server nodes
-    //     - content insertion, triggered by above-threshold
-    //       demand from clients
-    //
-    // All background jobs are scheduled on a queue of
-    // timed jobs (using an EmbargoedQueue), and picked off
-    // and passed to background threads.
-    // --------------------------------------
-
-    // --------------------------------------------
-    // HIGH-LEVEL TASK-SPECIFIC JOB SCHEDULING METHODS
-    // --------------------------------------------
-
-    public void scheduleStartupJobs()
-    {
-        Iterator peerRecs = peers.values().iterator();
-        while (peerRecs.hasNext()) {
-            QPeer peerRec = (QPeer)peerRecs.next();
-
-            // also, while we're here, schedule a 'getUpdate' update job
-            schedulePeerUpdateJob(peerRec);
-        }
-        
-        System.out.println("scheduleStartupJobs: c<p="+updateCatalogFromPeers+", isClient="+isClient);
-    }
-
-    public void scheduleShutdown()
-    {
-        Hashtable job = new Hashtable();
-        job.put("cmd", "shutdown");
-        runAfter(1000, job, "shutdown");
-    }
-
-    public void schedulePeerUploadJob(QDataItem item)
-    {
-        String uri = (String)item.get("uri");
-        Hashtable job = new Hashtable();
-        job.put("cmd", "uploadItem");
-        job.put("item", item);
-        job.put("uri", uri);
-        job.put("peersUploaded", new Hashtable()); // peerIds of peers we've uploaded to
-        job.put("peersPending", new Hashtable());  // peers we're trying to upload to
-        job.put("peersFailed", new Hashtable());   // peers which blew retry count
-        job.put("peersNumTries", new Hashtable()); // number of times we've tried given peer
-        String desc = "uploadItem:uri="+uri;
-        runNow(job, desc);
-    }
-
-    /**
-     * creates a job for inserting a single file locally
-     */
-    public void scheduleLocalInsertJob(String localPath, Hashtable metadata)
-    {
-        //System.out.println("sched: path='"+localPath+"' metadata="+metadata);
-        Hashtable job = new Hashtable();
-        job.put("cmd", "localPutItem");
-        job.put("metadata", metadata);
-        job.put("localDataFilePath", localPath);
-        //jobQueue.putNow(job);
-        String desc = "localPutItem:file="+localPath;
-        runNow(job, desc);
-    }
-
-    public void schedulePeerUpdateJob(QPeer peerRec)
-    {
-        long nextUpdateTime = peerRec.getTimeNextContact();
-        Hashtable job = new Hashtable();
-        String peerId = peerRec.getId();
-        job.put("cmd", "getUpdate");
-        job.put("peerId", peerId);
-        job.put("includeCatalog", new Integer(updateCatalogFromPeers));
-        job.put("includePeers", new Integer(1));
-        //jobQueue.putAt(nextUpdateTime, job);
-        long when = nextUpdateTime - new Date().getTime();
-        String desc = "getUpdate:peerID="+peerId+":when="+when;
-        runAt(nextUpdateTime, job, desc);
-    }
-
-    public void schedulePeerGreetingJob(QPeer peerRec)
-    {
-        String peerId = peerRec.getId();
-        if (peerRec.hasBeenGreeted() || peerId.equals(id)) {
-            return;
-        }
-
-        Hashtable job = new Hashtable();
-        job.put("cmd", "hello");
-        job.put("peerId", peerId);
-        //jobQueue.putNow(job);
-        String desc = "peerHello:peerID="+peerId;
-        runNow(job, desc);
-    }
-
-    public void scheduleTestJob(long delay, String msg)
-    {
-        Hashtable job = new Hashtable();
-        msg = msg + ":" + (new Date()).getTime();
-        job.put("cmd", "test");
-        job.put("msg", msg);
-        runAfter(delay, job, "test: "+msg);
-    }
-
-    // --------------------------------------------
-    // LOW-LEVEL JOB SCHEDULING METHODS
-    // --------------------------------------------
-
-    /**
-     * Add a job to the queue, to be executed immediately (or
-     * when a thread becomes available).
-     * Should make this protected at some time I guess.
-     * @param job a Hashtable containing job details
-     */
-    public void runNow(Hashtable job, String description)
-    {
-        runAfter(0, job, description);
-    }
-
-    /**
-     * Add a job to the queue, to be executed a given amount of time
-     * from now. Should make this protected at some time I guess.
-     * @param time number of milliseconds from now at which to run job
-     * @param job a Hashtable containing job details
-     */
-    public void runAfter(long time, Hashtable job, String description)
-    {
-        runAt(new Date().getTime()+time, job, description);
-    }
-
-    /**
-     * Add a job to the queue, to be executed at an absolute time.
-     * Should make this protected at some time I guess.
-     * @param time unixtime in milliseconds at which to run job
-     * @param job a Hashtable containing job details
-     */
-    public void runAt(long time, Hashtable job, String description)
-    {
-        String timeStr;
-        File jobFile;
-        String jobFilePath;
-
-        // serialise this job out to jobs dir
-        try {
-            // find unique jobtime
-            while (true) {
-                jobFilePath = jobsDir + sep + String.valueOf(time);
-                jobFile = new File(jobFilePath);
-                if (!jobFile.isFile()) {
-                    // got unique name
-                    break;
-                }
-                time += 1;
-            }
-
-            // got unique time - serialise our job to this time
-            timeStr = String.valueOf(time);
-            FileOutputStream out = new FileOutputStream(jobFilePath);
-            ObjectOutputStream objOut = new ObjectOutputStream(out);
-            objOut.writeObject(job);
-            objOut.close();
-
-            // also, write out the job description
-            SimpleFile sf = new SimpleFile(jobFilePath + ".desc", "rws");
-            sf.write(description);
-            
-        } catch (Exception e) {
-            e.printStackTrace();
-            return;
-        }
-
-        jobQueue.putAt(time, timeStr);
-    }
-    
-    /**
-     * Given a jobTimeString, reconstitutes a job from its serialisation
-     * in the node's jobs file directory.
-     * @param jobTime effectively serves as a jobId, since it's guaranteed to be unique
-     * @return the reconstituted job object, as a Hashtable
-     */
-    public Hashtable loadJob(String jobTime) throws Exception {
-
-        String jobFilePath = jobsDir + sep + jobTime;
-        File jobFile = new File(jobFilePath);
-        FileInputStream in = new FileInputStream(jobFilePath);
-        ObjectInputStream objIn = new ObjectInputStream(in);
-        Hashtable job = (Hashtable)objIn.readObject();
-        return job;
-    }
-
-    /**
-     * returns a Vector of currently queued jobs, with each element being
-     * a Hashtable containing keys 'time' and 'description'
-     */
-    public Vector getJobsList() throws Exception {
-
-        Vector jobs = new Vector();
-        long now = new Date().getTime() / 1000;
-
-        try {
-            String [] jobsFiles = new File(jobsDir).list();
-            for (int i=0; i<jobsFiles.length; i++) {
-                String jobFileName = jobsFiles[i];
-                if (!jobFileName.matches("[0-9]+\\.desc")) {
-                    continue;   // not a proper filename for a job object
-                }
-
-                String timeStr = jobFileName.substring(0, jobFileName.length()-5);
-                long time = new Long(timeStr).longValue() / 1000;
-                
-                
-                Hashtable job = new Hashtable();
-                String desc = new SimpleFile(jobsDir+sep+jobFileName, "r").read();
-                job.put("time", String.valueOf(time));
-                job.put("future", String.valueOf((time-now)));
-                job.put("description", desc);
-                jobs.addElement(job);
-            }
-        } catch (Exception e) {
-            
-        }
-
-        return jobs;
-    }
-
-    /**
-     * Loads the 'job description' string for a given jobTimeString
-     * @param jobTime effectively serves as a jobId, since it's guaranteed to be unique
-     * @return the job description, as a one-liner String
-     */
-    public String loadJobDescription(String jobTime) throws Exception {
-
-        String jobFilePath = jobsDir + sep + jobTime + ".desc";
-        String desc = new SimpleFile(jobFilePath, "r").read();
-        return desc;
-    }
-    
-    /**
-     * Manages the scheduled background tasks of this node.
-     * Do NOT invoke this directly
-     */
-    public void run()
-    {
-        log.info("Starting background tasks");
-        
-        isRunning = true;
-        
-        // mark our start time
-        nodeStartTime = new Date();
-        
-        // fire up our xml-rpc server interface
-        log.info("launching node XML-RPC server");
-        QServerMethods handler = new QServerMethods(this);
-        try {
-            startExternalInterfaces(handler);
-        } catch (Exception e) {
-            log.error("Failed to start external interfaces", e);
-            return;
-        }
-
-        // reconstitute the jobs queue from last run
-        try {
-            long now = new Date().getTime();
-            String [] oldJobs = new File(jobsDir).list();
-            for (int i=0; i<oldJobs.length; i++) {
-                try {
-                    String jobTimeStr = oldJobs[i];
-                    if (!jobTimeStr.matches("[0-9]+")) {
-                        continue;   // not a proper filename for a job object
-                    }
-
-                    long jobTime = new Long(jobTimeStr).longValue();
-
-                    jobQueue.putAt(jobTime, jobTimeStr);
-
-                } catch (Exception e) {
-                    e.printStackTrace();
-                }
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        
-        // a few dummy jobs to test the scheduler
-        scheduleTestJob(20000, "red");
-        scheduleTestJob(40000, "white");
-        scheduleTestJob(30000, "blue");
-
-        // fetch items from the job queue, and launch
-        // threads to execute them
-        while (isRunning)
-        {
-            // get a thread slot from the thread pool
-            try {
-                threadPool.acquire();
-            } catch (Exception e) {
-                e.printStackTrace();
-                log.warn("QNode.run: crashed on acquiring new thread");
-                continue;
-            }
-
-            // got a thread slot - now get (wait for) first available job
-            String jobTime = (String)jobQueue.get();
-            
-            // create and launch the thread for this job
-            Thread thrd = new QWorkerThread(this, jobTime);
-            thrd.start();
-        }
-    }
-    
-    // ----------------------------------------------------------
-    // METHODS for peer2peer communication
-    //
-    // These methods implement 6 basic primitives:
-    //  - ping
-    //  - hello
-    //  - getCatalog
-    //  - getPeerList
-    //  - getItem
-    //  - putItem
-    //
-    // Each of these primitives are executed as main, remote and
-    // local versions. On server nodes, the main version just forwards
-    // to the local version. On client nodes, the main version may
-    // try the local version, then a remote version.
-    // 
-    // For example, 'getItem'. The main entry point, 'getItem',
-    // on server nodes, just tries the local datastore.
-    // On client nodes, tries the lcoal datastore, and if this
-    // doesn't have the item, tries all remote peers whihc are
-    // known to have the item
-    // ----------------------------------------------------------
-
-    // ---------------------------------------
-    // PRIMITIVE - ping
-    // ---------------------------------------
-
-    /**
-     * local implementation of the method
-     */
-    public Hashtable ping()
-    {
-        int tNext = getAdvisedNextContactTime();
-        int dNext = tNext - (int)(new Date().getTime() / 1000);
-
-        Hashtable h = new Hashtable();
-        h.put("status", "ok");
-        h.put("id", id);
-        h.put("dest", destStr);
-        h.put("uptime", new Integer(nodeUptime()));
-        h.put("load", new Float(load_yPrev));
-        h.put("numPeers", new Integer(peers.size()));
-        h.put("numLocalItems", new Integer(localCatalogSize()));
-        h.put("timeNextContact", new Integer(tNext));
-        h.put("delayNextContact", new Integer(dNext));
-        h.put("numPendingUploads", new Integer(numPendingUploads));
-        if (isClient) {
-            h.put("numRemoteItems", new Integer(remoteCatalogSize()));
-        }
-        return h;
-    }
-
-    /**
-     * pings remote peer
-     */
-    public Hashtable peerPing(String peerId)
-        throws XmlRpcException, IOException, DataFormatException
-    {
-        return peerExecute(peerId, "ping", new Vector());
-    }
-
-    // ---------------------------------------
-    // PRIMITIVE - hello
-    // ---------------------------------------
-
-    /**
-     * introduces a remote destination as a peer
-     * @param dest destination of peer to add
-     */
-    public Hashtable hello(String dest64)
-    {
-        return localHello(dest64);
-    }
-
-    /**
-     * instructs remote node to add our dest to its peers list
-     */
-    public Hashtable peerHello(String peerId, String dest)
-        throws XmlRpcException, IOException, DataFormatException
-    {
-        Vector v = new Vector();
-        v.add(dest);
-
-        log.debug("peerHello: peerId="+peerId);
-        return peerExecute(peerId, "hello", v);
-    }
-
-    /**
-     * adds a new peer to this node's peers list
-     */
-     public Hashtable localHello(String destBase64)
-     {
-        Hashtable h = new Hashtable();
-
-        // one dirty dangerous hack here, we're assuming that I2PXmlRpcServer
-        // is not running multithreaded requests
-        //I2PSocket peerSocket = ((I2PXmlRpcServer)xmlRpcServer).sessSocket;
-        //Destination peerDest = peerSocket.getPeerDestination();
-        //String peerDestStr = peerDest.toBase64();
-        //log("localHello: peer dest="+peerDestStr);
-        //log("localHello: arg  dest="+destBase64);
-
-        try {
-            newPeer(destBase64);
-            h.put("status", "ok");
-        } catch (Exception e) {
-            e.printStackTrace();
-            h.put("status", "error");
-            h.put("error", e.getClass().getName());
-        }
-        return h;
-    }
-    
-    // ---------------------------------------
-    // PRIMITIVE - getUpdate
-    // ---------------------------------------
-
-    /**
-     * retrieves a peers/catalog update
-     */
-    public Hashtable getUpdate(int since, int includePeers, int includeCatalog)
-    {
-        return localGetUpdate(since, includePeers, includeCatalog);
-    }
-
-    /**
-     * gets a catalog update from remote peer
-     * @param peerid peer ID
-     * @param since Unixtime in seconds of last update request
-     * @param includePeers whether to include peers in update
-     * @param includeCatalog whether to include catalog in update
-     */
-    public Hashtable peerGetUpdate(String peerId, int since, int includePeers, int includeCatalog)
-        throws XmlRpcException, IOException, DataFormatException
-    {
-        Vector v = new Vector();
-        v.add(new Integer(since));
-        v.add(new Integer(includePeers));
-        v.add(new Integer(includeCatalog));
-
-        return peerExecute(peerId, "getUpdate", v);
-    }
-
-    /**
-     * returns portion of our catalog which is newer than
-     * given time
-     * @param since unixtime in seconds
-     * @param includePeers whether to include updated peers list
-     * @param includeCatalog whether to include updated catalog
-     */
-    public Hashtable localGetUpdate(int since, int includePeers, int includeCatalog)
-    {
-        Iterator items;
-        Hashtable h = new Hashtable();
-        int nowsecs;
-
-        Vector vCat = new Vector();
-        Vector vPeers = new Vector();
-
-        log.debug("localGetUpdate: since="+since+", incpeers="+includePeers+", inccat="+includeCatalog);
-        //System.out.println("localGetUpdate: since="+since+", incpeers="+includePeers+", inccat="+includeCatalog);
-
-        if (includeCatalog != 0) {
-            // get an iterator for all new catalog items since given unixtime
-            try {
-                items = contentIdx.getItemsSince(since);
-            } catch (IOException e) {
-                e.printStackTrace();
-                h.put("status", "error");
-                h.put("error", "IOException");
-                return h;
-            }
-
-            // pick through the iterator, and fetch metadata for each item
-            while (items.hasNext()) {
-                String key = (String)(items.next());
-                QDataItem item = getLocalMetadata(key);
-                //log.error("localGetUpdate: key="+key+", pf="+pf);
-                //System.out.println("localGetUpdate: key="+key+", pf="+pf);
-                if (item != null) {
-                    // clone this metadata, add in the key
-                    //Hashtable pf1 = (Hashtable)item.clone();
-                    //pf1.put("key", key);
-                    vCat.addElement(item);
-                }
-            }
-        }
-
-        if (includePeers != 0) {
-            try {
-                items = peersIdx.getItemsSince(since);
-            } catch (IOException e) {
-                e.printStackTrace();
-                h.put("status", "error");
-                h.put("error", "IOException");
-                return h;
-            }
-            while (true) {
-                try {
-                    String destId = (String)items.next();
-                    // get its dest
-                    try {
-                        QPeer peerRec = getPeerRecord(destId);
-                        vPeers.addElement(peerRec.destStr);
-                    } catch (Exception e) {
-                        log.warn("localGetPeersList", e);
-                    }
-                } catch (NoSuchElementException e) {
-                    break;
-                }
-            }
-        }
-
-        // finally - add update end time, and advised next update time
-        h.put("status", "ok");
-        h.put("items", vCat);
-        h.put("peers", vPeers);
-        h.put("timeUpdateEnds", new Integer(nowSecs())); // so peer knows where to update from next time
-        h.put("timeNextContact", new Integer(getAdvisedNextContactTime()));
-        return h;
-    }
-
-    // ---------------------------------------
-    // PRIMITIVE - getItem
-    // ---------------------------------------
-
-    /**
-     * <p>Retrieve an item of content.</p>
-     * <p>On server nodes this only retrieves from the local datastore.</p>
-     * <p>On client nodes, this tries the local datastore first, then
-     * attempts to get the data from remote servers believed to have the data</p>
-     */
-    public Hashtable getItem(String uri) throws IOException, QException
-    {
-        log.info("getItem: uri='"+uri+"'");
-        return localGetItem(uri);
-    }
-
-    /**
-     * retrieves an item of content from remote peer
-     */
-    public Hashtable peerGetItem(String peerId, String uri)
-        throws XmlRpcException, IOException, DataFormatException
-    {
-        Vector v = new Vector();
-        v.add(uri);
-
-        return peerExecute(peerId, "getItem", v);
-    }
-
-
-    /** returns true if this node possesses given key, false if not */
-    public boolean localHasItem(String uri) {
-        if (getLocalMetadata(uri) == null) {
-            return false;
-        }
-        else {
-            return true;
-        }
-    }
-
-    /** returns true if this node possesses given key, false if not */
-    public boolean localHasCatalogItem(String uri) {
-        if (getLocalCatalogMetadata(uri) == null) {
-            return false;
-        }
-        else {
-            return true;
-        }
-    }
-
-    /**
-     * returns the data stored under given key
-     */
-    public Hashtable localGetItem(String uri) throws IOException
-    {
-        log.info("localGetItem: uri='"+uri+"'");
-        Hashtable h = new Hashtable();
-
-        QDataItem item = getLocalMetadata(uri);
-        if (item == null)
-        {
-            // Honest, officer, we don't have it, we were just
-            // holding it for a friend!
-            System.out.println("localGetItem: no metadata for uri "+uri);
-            h.put("status", "error");
-            h.put("error", "notfound");
-            return h;
-        }
-
-        // locate the content
-        String dataHash = (String)item.get("dataHash");
-        String dataPath = makeDataPath(dataHash);
-        SimpleFile dataFile = new SimpleFile(dataPath, "r");
-
-        // barf if content missing
-        if (!dataFile.isFile())
-            {
-                System.out.println("localGetItem: no data for uri "+uri);
-                h.put("status", "error");
-                h.put("error", "missingdata");
-                return h;
-            }
-
-        // get data, hand it back with metadata
-        byte [] dataImage = dataFile.readBytes();
-        h.put("status", "ok");
-        h.put("metadata", item);
-        h.put("data", dataImage);
-        System.out.println("localGetItem: successful get: uri "+uri);
-        System.out.println("localGetItem: data hash="+sha256Base64(dataImage));
-        return h;
-    }
-
-    // ---------------------------------------
-    // PRIMITIVE - putItem
-    // ---------------------------------------
-
-    /**
-     * Insert an item of content, with no metadata
-     * @param raw data to insert
-     */
-    public Hashtable putItem(byte [] data) throws IOException, QException
-    {
-        return putItem(new Hashtable(), data);
-    }
-
-    /**
-     * Insert an item of content, with metadata
-     * overridden in client nodes
-     * @param metadata Hashtable of item's metadata
-     * @param data raw data to insert
-     */
-    public Hashtable putItem(Hashtable metadata, byte [] data) throws QException
-    {
-        Hashtable resp = new Hashtable();
-        QDataItem item;
-        try {
-            item = new QDataItem(metadata, data);
-            item.processAndValidate(false);
-            localPutItem(item);
-        } catch (QException e) {
-            resp.put("status", "error");
-            resp.put("error", "qexception");
-            resp.put("summary", e.getLocalizedMessage());
-            return resp;
-        }
-        
-        // success, it seems
-        resp.put("status", "ok");
-        resp.put("uri", (String)item.get("uri"));
-        return resp;
-    }
-
-    /**
-     * inserts an item of content to remote peer
-     */
-    public Hashtable peerPutItem(String peerId, byte [] data)
-        throws XmlRpcException, IOException, DataFormatException
-    {
-        Vector v = new Vector();
-        v.add(data);
-
-        return peerExecute(peerId, "putItem", v);
-    }
-
-    /**
-     * inserts an item of content to remote peer
-     */
-    public Hashtable peerPutItem(String peerId, Hashtable metadata, byte [] data)
-        throws XmlRpcException, IOException, DataFormatException
-    {
-        Vector v = new Vector();
-        v.add(metadata);
-        v.add(data);
-
-        return peerExecute(peerId, "putItem", v);
-    }
-
-    /**
-     * adds a new item of content to our local store, with given metadata
-     */
-    public void localPutItem(QDataItem item) throws QException
-    {
-        /**
-        // 1) hash the data, add to metadata
-        String dataHash = sha256Base64(data);
-        metadata.put("dataHash", dataHash);
-        System.out.println("localPutItem: dataHash="+dataHash);
-
-        // 2) if metadata has no key 'title', use hash as data
-        if (!metadata.containsKey("title"))
-        {
-            metadata.put("title", dataHash);
-        }
-
-        // 3) add size field to metadata
-        metadata.put("size", new Integer(data.length));
-        
-        // 4) get deterministic hash of final metadata
-        TreeSet t = new TreeSet(metadata.keySet());
-        Iterator keys = t.iterator();
-        int nkeys = t.size();
-        int i;
-        String metaStr = "";
-        for (i = 0; i < nkeys; i++)
-        {
-            String metaKey = (String)keys.next();
-            metaStr += metaKey + "=" + metadata.get(metaKey) + "\n";
-        }
-
-        // store the metadata and data
-        String metaPath = makeDataPath(metaHash+".meta");
-        String dataPath = makeDataPath(dataHash+".data");
-        new SimpleFile(dataPath, "rws").write(data);
-
-        PropertiesFile pf = new PropertiesFile(metaPath, metadata);
-
-        // update index
-        contentIdx.add(metaHash);
-
-        Hashtable h = new Hashtable();
-        h.put("status", "ok");
-        h.put("key", metaHash);
-        return h;
-
-        */
-
-        // work out where to store metadata and data
-        String metaFilename = item.getStoreFilename();
-        String metaPath = makeDataPath(metaFilename);
-        String dataPath = makeDataPath((String)item.get("dataHash"));
-
-        // store the data, if not already present
-        if (!(new File(dataPath).isFile())) {
-            byte [] data = item._data;
-            try {
-                new SimpleFile(dataPath, "rws").write(data);
-            } catch (Exception e) {
-                throw new QException("Error storing metadata", e);
-            }
-        }
-
-        // store metadata and add to index, if not already present
-        if (!(new File(metaPath).isFile())) {
-            try {
-                // store the metadata
-                PropertiesFile pf = new PropertiesFile(metaPath, item);
-            } catch (Exception e) {
-                throw new QException("Error storing data", e);
-            }
-
-            try {
-                // enter the metadata hash into our index
-                contentIdx.add(metaFilename);
-            } catch (Exception e) {
-                throw new QException("Error adding metadata to index", e);
-            }
-        }
-    }
-
-    // ---------------------------------------
-    // PRIMITIVE - newKeys
-    // ---------------------------------------
-
-    /**
-     * Generates a new keypair for signed-space insertions
-     * @return a struct with the keys:
-     * <ul>
-     *  <li>status - "ok"</li>
-     *  <li>publicKey - base64-encoded signed space public key</li>
-     *  <li>privateKey - base64-encoded signed space private key</li>
-     * </ul>
-     * When inserting an item using the privateKey, the resulting uri
-     * will be <code>Q:publicKey/path</code>
-     */
-    public Hashtable newKeys() {
-    
-        String [] keys = QUtil.newKeys();
-        Hashtable res = new Hashtable();
-        res.put("status", "ok");
-        res.put("publicKey", keys[0]);
-        res.put("privateKey", keys[1]);
-        return res;
-    }
-
-    // ---------------------------------------
-    // PRIMITIVE - search
-    // ---------------------------------------
-
-    /**
-     * Search datastore and catalog for a given item of content
-     * @param criteria
-     */
-    public Hashtable search(Hashtable criteria)
-    {
-        return localSearch(criteria);
-    }
-
-    public Hashtable localSearch(Hashtable criteria)
-    {
-        Hashtable result = new Hashtable();
-        result.put("status", "error");
-        result.put("error", "notimplemented");
-        return result;
-    }
-
-    public Hashtable insertQSite(String privKey64,
-                                 String siteName,
-                                 String rootPath,
-                                 Hashtable metadata
-                                 )
-        throws Exception
-    {
-        Hashtable result = new Hashtable();
-        result.put("status", "error");
-        result.put("error", "notimplemented");
-        return result;
-    }
-
-    /**
-     * returns true if all values in a given metadata set match their respective
-     * regexps in criteria.
-     * @param metadata a Hashtable of metadata to test. Set the 'magic' key 'searchmode'
-     * to 'or' to make this an or-based test, otherwise defaults to and-based test.
-     * @param criteria a Hashbable containing zero or more matching criteria
-     */
-    public boolean metadataMatchesCriteria(Hashtable metadata, Hashtable criteria)
-    {
-        boolean is_OrMode = false;
-
-        // search mode defaults to AND unless explicitly set to OR
-        if (criteria.containsKey("searchmode")) {
-            if (((String)criteria.get("searchmode")).toLowerCase().equals("or")) {
-                is_OrMode = true;
-            }
-        }
-
-        // test all keys and regexp values in criteria against metadata
-        Enumeration cKeys = criteria.keys();
-        while (cKeys.hasMoreElements()) {
-
-            String key = (String)cKeys.nextElement();
-            if (key.equals("searchmode")) {
-                // this is a meta-key - skip
-                continue;
-            }
-
-            String cval = (String)criteria.get(key);
-            String mval = (String)metadata.get(key);
-            if (mval == null) {
-                mval = "";
-            }
-            
-            //System.out.println("metadataMatchesCriteria: key='"+key+"'"
-            //    +" cval='"+cval+"'"
-            //    +" mval='"+mval+"'");
-
-            // reduced xor-based comparison
-            if (!(mval.matches(cval) ^ is_OrMode)) {
-                return is_OrMode;
-            }
-        }
-
-        // completed all
-        return !is_OrMode;
-    }
-
-    // ----------------------------------------------------------
-    // METHODS - datastore
-    // ----------------------------------------------------------
-
-    /**
-     * returns the number of known remote catalog entries
-     */
-    public int remoteCatalogSize()
-    {
-        return this.catalogIdx.numRecs;
-    }
-    
-    /**
-     * returns the number of locally stored items
-     */
-    public int localCatalogSize()
-    {
-        return this.contentIdx.numRecs;
-    }
-    
-    /** return a list of nodeIds containing a key, or null if none */
-    public Vector getItemLocation(String key) throws IOException {
-
-        String dir1 = key.substring(0, 1);
-        String dir2 = key.substring(0, 2);
-        String fullPath = locationDir + sep + dir1 + sep + dir2 + sep + key;
-        File fullFile = new File(fullPath);
-        File parent = fullFile.getParentFile();
-        if (!parent.isDirectory()) {
-            parent.mkdirs();
-        }
-        
-        if (!fullFile.exists()) {
-            return null;
-        }
-
-        String p = new SimpleFile(fullPath, "r").read().trim();
-        
-        String [] locs = p.split("\\s+");
-        Vector v = new Vector();
-        int i, nlocs=locs.length;
-        if (p.length() > 0) {
-            for (i=0; i<nlocs; i++) {
-                v.addElement(locs[i]);
-            }
-        }
-
-        return v;
-    }
-
-    /** record a peer nodeID as a source for a content key */
-    public void addItemLocation(String key, String nodeId) throws IOException
-    {
-        String dir1 = key.substring(0, 1);
-        String dir2 = key.substring(0, 2);
-        String fullPath = locationDir + sep + dir1 + sep + dir2 + sep + key;
-        File fullFile = new File(fullPath);
-        File parent = fullFile.getParentFile();
-        if (!parent.isDirectory()) {
-            parent.mkdirs();
-        }
-        
-        // got the full path - contents of file is a nodeId, one per line,
-        // that purportedly possesses the data
-        if (!fullFile.exists()) {
-            fullFile.createNewFile();
-        }
-        String p = new SimpleFile(fullPath, "r").read().trim();
-        
-        String [] locs = p.split("\\s+");
-        Vector v = new Vector();
-        int i, nlocs=locs.length;
-        if (p.length() > 0) {
-            for (i=0; i<nlocs; i++) {
-                v.addElement(locs[i]);
-            }
-        }
-        if (!v.contains(nodeId)) {
-            v.addElement(nodeId);
-            RandomAccessFile f = new RandomAccessFile(fullFile, "rw");
-            f.seek(f.length());
-            f.write((nodeId+"\n").getBytes());
-        }
-    }
-
-    /**
-     * retrieves the metadata for a given key
-     * Wparam key key to retrieve metadata for
-     * @return metadata as a PropertiesFile object, or null if not found
-     */
-    public QDataItem getLocalMetadata(String uri)
-    {
-        String filename = QUtil.sha64(uri);
-        return getLocalMetadataForHash(filename);
-    }
-    
-    public QDataItem getLocalMetadataForHash(String filename)
-    {
-        String metaPath = makeDataPath(filename);
-        SimpleFile f;
-        try {
-            f = new SimpleFile(metaPath, "r");
-        } catch (FileNotFoundException e) {
-            return null;
-        }
-
-        // got at least metadata
-        try {
-            PropertiesFile pf = new PropertiesFile(metaPath);
-            QDataItem item = new QDataItem(pf);
-            return item;
-
-        } catch (IOException e) {
-            return null;
-        }
-
-    }
-
-    /**
-     * retrieves the metadata for a given key in our remote catalog
-     * Wparam key key to retrieve metadata for
-     * @return metadata as a Hashtable object, or null if not found
-     */
-    public QDataItem getLocalCatalogMetadata(String uri)
-    {
-        String metaPath = makeCatalogPath(uri);
-        return getLocalCatalogMetadataForHash(metaPath);
-    }
-    
-    public QDataItem getLocalCatalogMetadataForHash(String metaPath)
-    {
-        SimpleFile f;
-        try {
-            f = new SimpleFile(metaPath, "r");
-        } catch (FileNotFoundException e) {
-            System.out.println("getLocalCatalogMetadata: no such file '"+metaPath+"'");
-            return null;
-        }
-
-        // got at least metadata
-        try {
-            PropertiesFile pf = new PropertiesFile(metaPath);
-            QDataItem item = new QDataItem(pf);
-            return item;
-
-        } catch (IOException e) {
-            e.printStackTrace();
-            return null;
-        }
-
-    }
-
-    /**
-     * called after a peerUpdate, stores one item of metadata to
-     * our local catalog
-     */
-    public void addMetadataToCatalog(Hashtable metadata) {
-
-        log.error("addMetadataToCatalog: metadata="+metadata);
-        //System.out.println("addMetadataToCatalog: metadata="+metadata);
-
-        String key = (String)metadata.get("key");
-        String catPath = makeCatalogPath(key);
-        
-        try {
-            new PropertiesFile(catPath, metadata);
-            catalogIdx.add(key);
-        } catch (IOException e) {
-            log.error("failed to add key '"+key+"' to catalog");
-        }
-
-    }
-
-    /**
-     * <p>determines an absolute pathname for storing an item of a
-     * given name. Uses multi-level directories in sourceforge style</p>
-     * <p>For instance, if name is 'blah', and node's data dir lives
-     * at /home/qserver/content, then the path will be /home/qserver/content/b/bl/blah.</p>
-     * <p>Note that directories are created as needed</p>
-     * @param name the filename to store
-     * @return the full pathname to write to
-     */
-    public String makeDataPath(String name)
-    {
-       String dir1 = name.substring(0, 1);
-       String dir2 = name.substring(0, 2);
-       String fullPath = contentDir + sep + dir1 + sep + dir2 + sep + name;
-       File fullFile = new File(fullPath);
-       File parent = fullFile.getParentFile();
-       if (!parent.isDirectory()) {
-           parent.mkdirs();
-       }
-       
-       // all done, parent dir now exists
-       return fullPath;
-    }
-
-    /**
-     * <p>determines an absolute pathname for cataloging an item of a
-     * given name. Uses multi-level directories in sourceforge style</p>
-     * <p>For instance, if name is 'blah', and node's data dir lives
-     * at /home/qserver/content, then the path will be /home/qserver/content/b/bl/blah.</p>
-     * <p>Note that directories are created as needed</p>
-     * @param name the filename to store
-     * @return the full pathname to write to
-     */
-    public String makeCatalogPath(String name)
-    {
-       String dir1 = name.substring(0, 1);
-       String dir2 = name.substring(0, 2);
-       String fullPath = catalogDir + sep + dir1 + sep + dir2 + sep + name;
-       File fullFile = new File(fullPath);
-       File parent = fullFile.getParentFile();
-       if (!parent.isDirectory()) {
-           parent.mkdirs();
-       }
-       
-       // all done, parent dir now exists
-       return fullPath;
-    }
-
-    
-    /**
-     * returns a PropertiesFile object for given peer
-     * @param peerId
-     * @return PropertiesFile object representing that peer's data
-     */
-    public QPeer getPeerRecord(String peerId) throws IOException, DataFormatException
-    {
-        // return peer's property object
-        return new QPeer(this, peerId);
-    }
-
-    /**
-     * Creates new peer record in our datastore
-     * @param dest64 String - destination in base64 format
-     */
-    public void newPeer(String dest64) throws IOException, DataFormatException
-    {
-        Destination d = new Destination();
-        d.fromBase64(dest64);
-        newPeer(d);
-    }
-    
-    /**
-     * Fetches/Creates new peer record in our datastore
-     */
-    public void newPeer(Destination peerDest) throws IOException
-    {
-        String peerDest64 = peerDest.toBase64();
-        
-        // bail if this new peer is self
-        if (peerDest64.equals(destStr)) {
-            return;
-        }
-
-        // determine peerID
-        String peerId = destToId(peerDest);
-
-        // bail if peer is already known
-        if (peers.containsKey(peerId)) {
-            log.debug("newPeer: already know peer "+peerId+" ("+peerDest64.substring(0, 12)+"...)");
-            return;
-        }
-
-        // where does the peer file live?
-        String peerPath = peersDir + sep + peerId;
-        
-        // get the record
-        QPeer peerRec = new QPeer(this, peerDest);
-
-        // and write it into index
-        peersIdx.add(peerId);
-        
-        // and stick into our global peers map
-        peers.put(peerId, peerRec);
-        
-        // note that we've got a new peer
-        conf.incrementIntProperty("numPeers");
-
-        // and, finally, schedule in a greeting to this peer
-        if (isClient) {
-            schedulePeerUpdateJob(peerRec);
-        } else {
-            schedulePeerGreetingJob(peerRec);
-        }
-    }
-
-    /**
-     * Get a list of peers, in order of their kademlia-closeness to
-     * a given uri
-     */
-    public Vector peersClosestTo(String uri, int max) {
-
-        String itemHash = sha256Base64(uri);
-
-        // get our peer list as a vector
-        Vector allPeers = new Vector();
-        Iterator peerRecs = peers.values().iterator();
-        while (peerRecs.hasNext()) {
-            allPeers.addElement(peerRecs.next());
-        }
-
-        // create a comparator to find peers closest to URI
-        QKademliaComparator comp = new QKademliaComparator(this, itemHash);
-        
-        // sort the peerlist according to k-closeness of uri
-        Collections.sort(allPeers, comp);
-
-        // get the closest (up to) n peers
-        int npeers = Math.min(max, allPeers.size());
-        List closestPeers = allPeers.subList(0, npeers);
-        
-        return new Vector(closestPeers);
-    }
-
-    // ----------------------------------------------------------
-    // METHODS - node status indicators
-    // ----------------------------------------------------------
-    
-    /** return uptime of this node, in seconds */
-    public int nodeUptime()
-    {
-        Date now = new Date();
-        return (int)((now.getTime() - nodeStartTime.getTime()) / 1000);
-    }
-
-    /** return node load, as float */
-    public float nodeLoad()
-    {
-        long now = new Date().getTime();
-        long dt = now - load_tPrev;
-        load_tPrev = now;
-        
-        //System.out.println("nodeLoad: dt="+dt+" load_yPrev="+load_yPrev);
-        
-        load_yPrev = load_yPrev * Math.exp(-((double)dt) / load_kFall);
-        
-        //System.out.println("nodeLoad: y="+load_yPrev);
-        
-        return (float)load_yPrev;
-    }
-
-    public float nodeLoadAfterHit()
-    {
-        //System.out.println("nodeLoadAfterHit: "+load_yPrev+" before recalc");
-        // update decay phase
-        nodeLoad();
-        
-        //System.out.println("nodeLoadAfterHit: "+load_yPrev+" after recalc");
-        
-        // and add spike
-        load_yPrev += (1.0 - load_yPrev) / load_kRise;
-        
-        //System.out.println("nodeLoadAfterHit: "+load_yPrev+" after hit");
-        //System.out.println("-----------------------------------------");
-        
-        return (float)load_yPrev;
-    }
-
-    /**
-     * Determine an advised time for next contact from a peer node.
-     * This is based on the node's current load
-     */
-    public int getAdvisedNextContactTime()
-    {
-        //long now = new Date().getTime() / 1000;
-        // fudge 30 secs from now
-        //return (int)(now + 30);
-
-        // formula here is to advise a backup delay of:
-        //   loadBackoffMin + 2 ** (loadBackoffBits * currentLoad)
-        return nowSecs()
-            + load_backoffMin
-            + (int)(Math.pow(load_backoffBase, load_backoffBits * load_yPrev));
-    }
-
-
-    // ----------------------------------------------------------
-    // METHODS - general
-    // ----------------------------------------------------------
-
-    public String base64Enc(String raw)
-    {
-        return base64Enc(raw.getBytes());
-    }
-    
-    public String base64Enc(byte[] raw)
-    {
-        return net.i2p.data.Base64.encode(raw);
-    }
-
-    public String base64Dec(String enc)
-    {
-        return new String(net.i2p.data.Base64.decode(enc));
-    }
-
-    public String sha256Base64(String raw)
-    {
-        return sha256Base64(raw.getBytes());
-    }
-
-    public String sha256Base64(byte [] raw)
-    {
-        //return base64Enc(sha256(raw));
-        return base64Enc(i2p.sha().calculateHash(raw).getData()).replaceAll("[=]+", "");
-    }
-
-    /**
-     * simple interface for sha256 hashing
-     * @param raw a String to be hashed
-     * @return the sha256 hash, as binary
-     */
-    public String sha256(String raw)
-    {
-        return sha256(raw.getBytes());
-    }
-
-    public String sha256(byte [] raw)
-    {
-        return new String(i2p.sha().calculateHash(raw).getData());
-        
-        //SHA256Generator shagen = new SHA256Generator(i2p);
-        //return new String(shagen.calculateHash(raw).getData());
-        //Sha256 s = new Sha256();
-        //s.update(raw, 0, raw.length);
-        //byte [] d = s.digest();
-        //for (int i=0; i<d.length; i++) {
-        //    System.out.print(d[i]+",");
-        //}
-        //System.out.println("");
-        //return new String(d);
-    }
-
-    public int nowSecs() {
-        return (int)(new Date().getTime() / 1000);
-    }
-
-    public String destToId(String destStr)
-    {
-        String destRaw = base64Dec(destStr);
-        return sha256Base64(destRaw);
-    }
-
-    /**
-     * computes the node ID of a given destination
-     */
-    public String destToId(Destination d)
-    {
-        return sha256Base64(d.toString());
-    }
-
-    /** formats a positive int n to witdh chars
-     * @param n integer to format to string, should be >= 0
-     * @param width minimum width of string, which will get padded
-     * with leading zeroes to make up the desired width
-     */
-    public String intFmt(int n, int width)
-    {
-        String nS = String.valueOf(n);
-        while (nS.length() < width)
-        {
-            nS = "0" + nS;
-        }
-        return nS;
-    }
-
-    public void log__(String msg)
-    {
-        System.out.println("QNode: " + msg);
-
-        // bail if logFile not yet created, can help in avoiding npe
-        if (logFile == null) {
-            return;
-        }
-
-        try {
-            Calendar now = Calendar.getInstance();
-            String timestamp
-                = intFmt(now.YEAR, 4)
-                + "-"
-                + intFmt(now.MONTH, 2)
-                + "-"
-                + intFmt(now.DAY_OF_MONTH, 2)
-                + "-"
-                + intFmt(now.HOUR_OF_DAY, 2)
-                + ":"
-                + intFmt(now.MINUTE, 2)
-                + ":"
-                + intFmt(now.SECOND, 2)
-                + " ";
-
-            synchronized (logFile) {
-                logFile.seek(logFile.length());
-                logFile.write((timestamp + msg + "\n").getBytes());
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-
-    }
-
-    public void dj() {
-        dumpjobs();
-    }
-
-    public void dumpjobs() {
-        
-        jobQueue.printWaiting();
-    }
-
-    public void foo() {
-        System.out.println("QNode.foo: isClient="+isClient);
-    }
-
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/q/QPeer.java b/apps/q/java/src/net/i2p/aum/q/QPeer.java
deleted file mode 100644
index 83fe2311a3a6cc812d1f52a2acb2435f82deb591..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QPeer.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * QPeer.java
- *
- * Created on March 28, 2005, 2:13 PM
- */
-
-package net.i2p.aum.q;
-
-import java.io.IOException;
-import java.io.Serializable;
-
-import net.i2p.aum.PropertiesFile;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Destination;
-
-/**
- * Wrapper for a peer record file.
- * Implements a bunch of accessor methods for getting/setting numerical attribs
- */
-public class QPeer implements Serializable {
-
-    QNode node;
-    protected Destination dest;
-    protected String peerId;
-    protected String destStr;
-
-    public PropertiesFile file;
-
-    /** Creates a whole new peer */
-    public QPeer(QNode node, Destination dest) throws IOException {
-
-        file = new PropertiesFile(node.peersDir + node.sep + node.destToId(dest));
-
-        this.dest = dest;
-        destStr = dest.toBase64();
-        peerId = node.destToId(dest);
-        
-        file.setProperty("id", peerId);
-        file.setProperty("dest", destStr);
-        file.setProperty("timeLastUpdate", "0");
-        file.setProperty("timeLastContact", "0");
-        file.setProperty("timeNextContact", "0");
-    }
-
-    /** Loads an existing peer, barfs if nonexistent */
-    public QPeer(QNode node, String destId) throws IOException, DataFormatException {
-        
-        file = new PropertiesFile(node.peersDir + node.sep + destId);
-        
-        // barf if file doesn't exist
-        if (!file._fileExists) {
-            throw new IOException("Missing peer record file");
-        }
-
-        destStr = file.getProperty("dest");
-        dest = new Destination();
-        dest.fromBase64(destStr);
-        peerId = destId;
-    }
-
-    public Destination getDestination() {
-        return dest;
-    }
-
-    public String getDestStr() {
-        return destStr;
-    }
-    
-    public String getId() {
-        return peerId;
-    }
-
-    public int getTimeLastUpdate() {
-        return new Integer(file.getProperty("timeLastUpdate")).intValue();
-    }
-
-    public void setTimeLastUpdate(long when) {
-        file.setProperty("timeLastUpdate", String.valueOf(when));
-    }
-
-    public int getTimeLastContact() {
-        return new Integer(file.getProperty("timeLastContact")).intValue();
-    }
-
-    public void setTimeLastContact(int when) {
-        file.setProperty("timeLastContact", String.valueOf(when));
-    }
-
-    public int getTimeNextContact() {
-        return new Integer(file.getProperty("timeNextContact")).intValue();
-    }
-
-    public void setTimeNextContact(int when) {
-        file.setProperty("timeNextContact", String.valueOf(when));
-    }
-    
-    public boolean hasBeenGreeted() {
-        return file.containsKey("sentHello");
-    }
-
-    public void markAsGreeted() {
-        file.setProperty("sentHello", "1");
-    }
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/q/QServerMethods.java b/apps/q/java/src/net/i2p/aum/q/QServerMethods.java
deleted file mode 100644
index b06c22817d380857641b655b7a59625c13474578..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QServerMethods.java
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * QServerMethods.java
- *
- * Created on 20 March 2005, 23:23
- */
-
-package net.i2p.aum.q;
-
-import java.io.IOException;
-import java.util.Hashtable;
-import java.util.Vector;
-
-
-/**
- * Defines the methods which will be exposed in the server's
- * XML-RPC interface. On the xml-rpc client side, these methods are invoked
- * through the 'peerXXXX' methods.
- * This class is just a shim, which invokes methods of the same name on
- * the QServerNode. It's separated off as a shim because the XML-RPC implementation
- * we're using (org.apache.xmlrpc) can only add entire objects and all their
- * methods as handlers, and doesn't support adding a-la-carte methods.
- */
-public class QServerMethods {
-
-    private QNode node;
-
-    /**
-     * Creates a new instance of QServerMethods,
-     * with a ref to the server
-     */
-    public QServerMethods(QNode node) {
-        this.node = node;
-    }
-    
-    /**
-     * pings this peer node
-     */
-    public Hashtable ping() {
-        node.nodeLoadAfterHit();
-        System.out.println("XMLRPC: ping");
-        return node.ping();
-    }
-
-    /**
-     * pings this peer node
-     * @param args a Hashtable (dict, struct, assoc array) of args, all of which are
-     * completely ignored
-     */
-    public Hashtable ping(Hashtable args) {
-        return ping();
-    }
-
-    /**
-     * introduces ourself to this remote peer. From then on, caller will be expected
-     * to maintain reasonable uptime
-     * @param destStr our own base64 destination
-     */
-    public Hashtable hello(String destStr) {
-        node.nodeLoadAfterHit();
-        System.out.println("XMLRPC: hello");
-        return node.hello(destStr);
-    }
-
-    /**
-     * introduces ourself to this remote peer. From then on, caller will be expected
-     * to maintain reasonable uptime
-     * @param args a Hashtable/dict/struct/assoc-array containing:
-     * <ul>
-     *  <li>dest - base64 destination (noderef) for the remote peer to add</li>
-     * </ul>
-     */
-    public Hashtable hello(Hashtable args) {
-        String destStr;
-        System.out.println("XMLRPC: hello");
-        try {
-            destStr = (String)args.get("dest");
-        } catch (Exception e) {
-            destStr = null;
-        }
-        if (destStr == null) {
-            Hashtable res = new Hashtable();
-            res.put("status", "error");
-            res.put("error", "baddest");
-            res.put("summary", "Bad or missing destination");
-            node.nodeLoadAfterHit();
-            return res;
-        }
-        return hello(destStr);
-    }
-
-    /**
-     * Searches node for all data items whose metadata keys match the keys
-     * of the given mapping.
-     * @param criteria a Hashtable (or python dict, etc) of search criteria. Each
-     * 'key' is a metadata item to match, and corresponding value is a regular expression
-     * to match.
-     */
-    public Hashtable search(Hashtable criteria) {
-        node.nodeLoadAfterHit();
-        System.out.println("XMLRPC: search");
-        System.out.println("XMLRPC: search: "+criteria);
-        return node.search(criteria);
-    }
-
-    /**
-     * returns a list of new content and/or peers which have
-     * been stored on the server since a given time
-     * @param since (int) unixtime in seconds
-     * @param includePeers (int) set to 1 to include 'peers' list in update, 0 to omit
-     * @param includeCatalog (int) set to 1 to include 'items' (catalog) list in
-     * update, 0 to omit
-     */
-    public Hashtable getUpdate(int since, int includePeers, int includeCatalog) {
-        node.nodeLoadAfterHit();
-        System.out.println("XMLRPC: getUpdate: "+since+" "+includePeers+" "+includeCatalog);
-        return node.getUpdate(since, includePeers, includeCatalog);
-    }
-
-    /**
-     * returns a list of new content and/or peers which have
-     * been stored on the server since a given time
-     * Wparam args a Hashtable/struct/dict/assoc-array of arguments, including:
-     * <ul>
-     *   <li>since - (int) unixtime in seconds</li>
-     *   <li>includePeers - (int) set to nonzero to include 'peers' list in update, 0 to omit,
-     *      default 0</li>
-     *   <li>includeCatalog - (int) set to nonzero to include 'items' (catalog) list in
-     *      update, 0 to omit (default 0)</li>
-     * </ul>
-     */
-    public Hashtable getUpdate(Hashtable args) {
-        int since;
-        int includePeers = 0;
-        int includeCatalog = 0;
-
-        // uplift 'since' key from args, or barf if invalid
-        try {
-            since = ((Integer)(args.get("since"))).intValue();
-        } catch (Exception e) {
-            Hashtable res = new Hashtable();
-            res.put("status", "error");
-            res.put("error", "badargument");
-            res.put("summary", "Invalid value for 'since'");
-            node.nodeLoadAfterHit();
-            return res;
-        }
-
-        // uplift 'includePeers' key from args, silently fall back
-        // on default if invalid
-        if (args.containsKey("includePeers")) {
-            try {
-                includePeers = ((Integer)(args.get("includePeers"))).intValue();
-            } catch (Exception e) {}
-        }
-
-        // uplift 'includeCatalog' key from args, silently fall back
-        // on default if invalid
-        if (args.containsKey("includeCatalog")) {
-            try {
-                includeCatalog = ((Integer)(args.get("includeCatalog"))).intValue();
-            } catch (Exception e) {}
-        }
-        return getUpdate(since, includePeers, includeCatalog);
-    }
-
-    public Vector getJobsList() throws Exception {
-        return node.getJobsList();
-    }
-
-    /**
-     * attempt to retrieve a data item from remote peer
-     * @param key - the key under which the content item is assumedly stored in Q
-     */
-    public Hashtable getItem(String uri) throws IOException, QException {
-        node.nodeLoadAfterHit();
-        System.out.println("XMLRPC: getItem: "+uri);
-        return node.getItem(uri);
-    }
-
-    /**
-     * attempt to retrieve a data item from remote peer
-     * @param args - a Hashtable/struct/dict/assoc-array, containing:
-     *  <ul>
-     *    <li>key - (string) the key under which the content item is assumedly stored in Q</li>
-     *  </ul>
-     */
-    public Hashtable getItem(Hashtable args) throws IOException, QException {
-        String key;
-        try {
-            key = (String)args.get("key");
-        } catch (Exception e) {
-            Hashtable res = new Hashtable();
-            res.put("status", "error");
-            res.put("error", "badargs");
-            node.nodeLoadAfterHit();
-            return res;
-        }
-
-        return getItem(key);
-    }
-
-    /**
-     * puts an item of content to remote peer
-     * @param args - a Hashtable/struct/dict/assoc-array, containing at least:
-     *  <ul>
-     *   <li>data - binary - the raw data to insert</li>
-     *  </ul>
-     * Any other key/value pairs in this struct will be taken as metadata, and
-     * inserted into the datastore as such.
-     * @return the assigned key for the item, under which the item
-     * can be subsequently retrieved. This key will be inserted into 
-     * the metadata
-     */
-    public Hashtable putItem(Hashtable args)
-        throws IOException, QException
-    {
-        byte [] data;
-        try {
-            data = (byte [])args.get("data");
-            args.remove("data");
-        } catch (Exception e) {
-            Hashtable res = new Hashtable();
-            res.put("status", "error");
-            res.put("error", "baddata");
-            node.nodeLoadAfterHit();
-            return res;
-        }
-        return putItem(args, data);
-    }
-
-    /**
-     * alternative wrapper method which allows data to be a String.
-     * DO NOT USE if the string contains any control chars or bit-7-set chars
-     */
-    public Hashtable putItem(Hashtable metadata, String data)
-        throws IOException, QException
-    {
-        return putItem(metadata, data.getBytes());
-    }
-
-    /**
-     * alternative wrapper method which allows data to be a String.
-     * DO NOT USE if the string contains any control chars or bit-7-set chars
-     */
-    public Hashtable putItem(String data)
-        throws IOException, QException
-    {
-        return putItem(data.getBytes());
-    }
-
-
-    /**
-     * puts an item of content to remote peer
-     * Wparam metadata a mapping object containing metadata
-     * @param data raw data to insert
-     * @return the assigned key for the item, under which the item
-     * can be subsequently retrieved. This key will be inserted into 
-     * the metadata
-     */
-    public Hashtable putItem(Hashtable metadata, byte [] data)
-        throws IOException, QException
-    {
-        node.nodeLoadAfterHit();
-        System.out.println("XMLRPC: putItem: "+metadata);
-        return node.putItem(metadata, data);
-    }
-
-    /**
-     * puts an item of data, without metadata, into the network
-     * @param data - binary - the raw data to insert
-     * @return the assigned key for the item
-     */
-    public Hashtable putItem(byte [] data)
-        throws IOException, QException
-    {
-        node.nodeLoadAfterHit();
-        System.out.println("XMLRPC: putItem (no metadata)");
-        return node.putItem(data);
-    }
-
-    /**
-     * Schedules the insertion of a qsite. Valid for client nodes only
-     * @param privKey64 base64 representation of a signed space private key
-     * @param siteName short text name of the qsite, whose URI will end up
-     * as 'Q:pubKey64/siteName/'.
-     * @param rootPath physical absolute pathname of the qsite's root directory
-     * on the host filesystem.
-     * Note that this directory must have a file called 'index.html' at its top
-     * level, which will be used as the qsite's default document.
-     * @param metadata A set of metadata to associate with the qsite
-     * @return Hashtable containing results, as the keys:
-     * <ul>
-     *  <li>status - String - either "ok" or "error"</li>
-     *  <li>error - String - short summary of error, only present if
-     *    status is "error"</li>
-     *  <li>uri - the full Q URI for the top level of the site
-     * </ul>
-     */
-    public Hashtable insertQSite(String privKey64,
-                                 String siteName,
-                                 String rootPath,
-                                 Hashtable metadata
-                                 )
-        throws Exception
-    {
-        node.nodeLoadAfterHit();
-        System.out.println("XMLRPC: insertQSite("+privKey64+", "+siteName+", "+rootPath+", "+metadata+")");
-        return node.insertQSite(privKey64, siteName, rootPath, metadata);
-    }
-
-    /**
-     * Generates a new keypair for signed-space insertions
-     * @return a struct with the keys:
-     * <ul>
-     *  <li>status - "ok"</li>
-     *  <li>publicKey - base64-encoded signed space public key</li>
-     *  <li>privateKey - base64-encoded signed space private key</li>
-     * </ul>
-     * When inserting an item using the privateKey, the resulting uri
-     * will be <code>Q:publicKey/path</code>
-     */
-    public Hashtable newKeys() {
-
-        return node.newKeys();
-    }
-
-    /**
-     * shuts down the node
-     * for the purpose of security, the caller must quote the node's full
-     * base64 private key
-     * @param nodePrivKey the node's full base64 I2P private key
-     * @return if shutdown succeeds, an XML-RPC error will result, because
-     * the node will fail to send a reply. If an invalid key is given,
-     * the reply Hashtable will contain {"status":"error", "error":"invalidkey"}
-     */
-    public Hashtable shutdown(String nodePrivKey) {
-
-        Hashtable res = new Hashtable();
-
-        // sekkret h4x - kill the VM if key is the node's I2P base64 privkey
-        //System.out.println("shutdown: our privkey="+node.privKeyStr);
-        //System.out.println("shutdown: nodePrivKey="+nodePrivKey);
-        if (nodePrivKey.equals(node.privKeyStr)) {
-
-            res.put("status", "ok");
-            //node.scheduleShutdown();
-            // get a runtime
-            //System.out.println("Node at "+node.dataDir+" shutting down");
-            Runtime r = Runtime.getRuntime();
-            // and terminate the vm
-            //r.exit(0);
-            r.halt(0);
-        }
-        else {
-            res.put("status", "error");
-            res.put("error", "invalidkey");
-        }
-
-        return res;
-    }
-
-    /**
-     * shuts down the node
-     * for the purpose of security, the caller must quote the node's full
-     * base64 private key
-     * @param args - a Hashtable/struct/dict/assoc-array, containing:
-     *  <ul>
-     *    <li>privKey - string - the node's full base64 I2P private key</li>
-     *  </ul>
-     * @return if shutdown succeeds, an XML-RPC error will result, because
-     * the node will fail to send a reply. If an invalid key is given,
-     * the reply Hashtable will contain {"status":"error", "error":"invalidkey"}
-     */
-    public Hashtable shutdown(Hashtable args) {
-        String privKey;
-        try {
-            privKey = (String)args.get("privKey");
-        } catch (Exception e) {
-            Hashtable res = new Hashtable();
-            res.put("status", "error");
-            res.put("error", "badkey");
-            node.nodeLoadAfterHit();
-            return res;
-        }
-        return shutdown(privKey);
-    }
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/q/QServerNode.java b/apps/q/java/src/net/i2p/aum/q/QServerNode.java
deleted file mode 100644
index 1b91e652d8864f14365ceb3d7adfb3159c0bec60..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QServerNode.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * QServer.java
- *
- * Created on 20 March 2005, 23:23
- */
-
-package net.i2p.aum.q;
-
-import java.io.IOException;
-import java.util.Properties;
-
-import net.i2p.I2PException;
-import net.i2p.aum.I2PXmlRpcServerFactory;
-import net.i2p.aum.http.I2PHttpServer;
-import net.i2p.aum.http.MiniHttpServer;
-import net.i2p.data.DataFormatException;
-
-/**
- *
- * Implements Q Server nodes.
- */
-public class QServerNode extends QNode {
-
-    /**
-     * default datastore directory
-     */
-    public static String defaultStoreDir = ".quartermaster_server";
-
-    /**
-     * can set this to 0 before instantiating servers, to set tunnel length
-     * for debugging purposes
-     **/
-    public static int tunLength = 2;
-
-    public I2PXmlRpcServerFactory xmlRpcServerFactory;
-
-    public String nodeType = "Server";
-
-    /** Creates a new instance of QServer */
-    public QServerNode() throws IOException, DataFormatException, I2PException
-    {
-        super(System.getProperties().getProperty("user.home") + sep + defaultStoreDir);
-    }
-    
-    /**
-     * Creates a Q node in server mode, using specified datastore directory
-     * @param dataDir absolute pathname where this server's datastore tree is
-     * located. If tree doesn't exist, it will be created along with new keys
-     */
-    public QServerNode(String dataDir) throws IOException, DataFormatException, I2PException
-    {
-        super(dataDir);
-    }
-
-    /**
-     * performs mode-specific node setup
-     */
-    public void setup() throws DataFormatException, I2PException
-    {
-    }
-
-    /**
-     * <p>Sets up and launches an xml-rpc server for servicing requests
-     * to this node.</p>
-     * <p>For server nodes, the xml-rpc server listens within I2P on the
-     * node's destination.</p>
-     * <p>For client nodes, the xml-rpc server listens on a local TCP
-     * port (according to attributes xmlRpcServerHost and xmlRpcServerPort)</p>
-     */
-    public void startExternalInterfaces(QServerMethods methods) throws Exception {
-        /**
-         * // get a server factory if none already existing
-         * if (xmlRpcServerFactory == null) {
-         * getTunnelLength();
-         * log.info("Creating an xml-rpc server factory with tunnel length "+tunLength);
-         * xmlRpcServerFactory = new I2PXmlRpcServerFactory(
-         * tunLength, tunLength, tunLength, tunLength, i2p);
-         * }
-         *
-         * log.info("Creating XML-RPC server listening within i2p");
-         * xmlRpcServer = xmlRpcServerFactory.newServer(privKey);
-         *
-         * // bind in our interface class
-         * log.info("Binding XML-RPC interface object");
-         * xmlRpcServer.addHandler(baseXmlRpcServiceName, methods);
-         *
-         * // and fire it up
-         * log.info("Launching XML-RPC server");
-         * xmlRpcServer.start();
-         **/
-
-        Properties httpProps = new Properties();
-
-        httpProps = new Properties();
-        Properties sysProps = System.getProperties();
-        String i2cpHost = sysProps.getProperty("i2cp.tcp.host", "127.0.0.1");
-        String i2cpPort = sysProps.getProperty("i2cp.tcp.port", "7654");
-        httpProps.setProperty("i2cp.tcp.host", i2cpHost);
-        httpProps.setProperty("i2cp.tcp.port", i2cpPort);
-
-        // create in-i2p http server for xmlrpc and browser access
-        MiniHttpServer webServer = new I2PHttpServer(privKey, QClientWebInterface.class, this, httpProps);
-        webServer.addXmlRpcHandler(baseXmlRpcServiceName, methods);
-        webServer.start();
-        System.out.println("Started in-i2p http/xmlrpc server listening on dest:");
-        String dest = privKey.getDestination().toBase64();
-        System.out.println(dest);
-        
-    }
-    
-    public void getTunnelLength()
-    {
-        String tunLenStr = System.getProperty("quartermaster.tunnelLength");
-        if (tunLenStr == null)
-        {
-            return;
-        }
-
-        tunLength = new Integer(tunLenStr).intValue();
-    }
-
-    /**
-     * @param args the command line arguments
-     */
-    public static void main(String[] args) {
-    
-        QServerNode node;
-
-        try {
-            if (args.length > 0) {
-                node = new QServerNode(args[0]);
-            }
-            else {
-                node = new QServerNode();
-            }
-            node.log.info("QServerNode: entering endless loop...");
-            while (true) {
-                Thread.sleep(1000);
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-            System.exit(1);
-        }
-    }
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/q/QTest.java b/apps/q/java/src/net/i2p/aum/q/QTest.java
deleted file mode 100644
index 0c5dbfd6356d5a6e4cfb493d66959dddbddb1af5..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * QTest.java
- *
- * Created on March 23, 2005, 11:34 PM
- */
-
-package net.i2p.aum.q;
-
-import java.io.IOException;
-import java.util.Hashtable;
-
-import net.i2p.I2PException;
-import net.i2p.data.DataFormatException;
-
-
-/**
- *
- * @author  david
- */
-public class QTest {
-
-    QServerNode server;
-    
-    QClientNode client;
-
-    /** Creates a new instance of QTest */
-    public QTest() {
-    }
-
-    /**
-     * performs a series of tests on client node
-     */
-    public void testClientNode()
-        throws IOException, DataFormatException, I2PException, QException
-    {
-        print("Creating new client node");
-        QClientNode node = new QClientNode();
-        
-        print("Starting node background stuff");
-        node.start();
-        
-        print("Inserting new plain hash data item");
-        byte [] data = "Hello, world".getBytes();
-        Hashtable meta = new Hashtable();
-        meta.put("title", "simple test");
-        meta.put("type", "text");
-        meta.put("path", "/test.txt");
-        Hashtable res = node.putItem(meta, data);
-        print("putItem result="+res);
-        if (!res.get("status").equals("ok")) {
-            print("putItem fail: error="+res.get("error"));
-            node.interrupt();
-            return;
-        }
-
-        String uri = (String)res.get("uri");
-        print("putItem successful: uri="+uri);
-        
-        print("now attempting to retrieve");
-        Hashtable res1 = node.getItem(uri);
-        print("getItem: result="+res1);
-        if (!res1.get("status").equals("ok")) {
-            print("getItem fail: error="+res.get("error"));
-            node.interrupt();
-            return;
-        }
-        byte [] data1 = (byte [])res1.get("data");
-        String dataStr = new String(data1);
-        print("getItem: success, data="+dataStr);
-
-        print("now searching for what we just inserted");
-        Hashtable crit = new Hashtable();
-        crit.put("type", "text");
-        Hashtable res1a = node.search(crit);
-        print("After search: res="+res1a);
-        
-        print("now creating a keypair");
-        Hashtable keys = node.newKeys();
-        String pub = (String)keys.get("publicKey");
-        String priv = (String)keys.get("privateKey");
-        print("public="+pub);
-        print("private="+priv);
-        
-        print("Inserting new secure space data item");
-        byte [] data2 = "The quick brown fox".getBytes();
-        Hashtable meta2 = new Hashtable();
-        meta2.put("title", "simple test 2");
-        meta2.put("type", "text");
-        meta2.put("path", "/test.txt");
-        meta2.put("privateKey", priv);
-        Hashtable res2 = node.putItem(meta2, data2);
-        print("putItem result="+res2);
-        if (!res2.get("status").equals("ok")) {
-            print("putItem fail: error="+res2.get("error"));
-            node.interrupt();
-            return;
-        }
-
-        String uri2 = (String)res2.get("uri");
-        print("putItem successful: uri="+uri2);
-        
-        print("now attempting to retrieve");
-        Hashtable res2a = node.getItem(uri2);
-        print("getItem: result="+res2a);
-        if (!res2a.get("status").equals("ok")) {
-            print("getItem fail: error="+res.get("error"));
-            node.interrupt();
-            return;
-        }
-        byte [] data2a = (byte [])res2a.get("data");
-        String dataStr2a = new String(data2a);
-        print("getItem: success, data="+dataStr2a);
-        
-    }
-
-    public void print(String msg) {
-        System.out.println(msg);
-    }
-
-    /**
-     * @param args the command line arguments
-     */
-    public static void main(String[] args) {
-        QTest test = new QTest();
-        try {
-            test.testClientNode();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-}
-
diff --git a/apps/q/java/src/net/i2p/aum/q/QUtil.java b/apps/q/java/src/net/i2p/aum/q/QUtil.java
deleted file mode 100644
index 5b1ccec2cc73c4e15866bd8942401887b607b3cf..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QUtil.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * QUtil.java
- *
- * Created on April 6, 2005, 2:11 PM
- */
-
-package net.i2p.aum.q;
-
-import net.i2p.I2PAppContext;
-import net.i2p.data.Base64;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.SigningPrivateKey;
-import net.i2p.data.SigningPublicKey;
-
-/**
- * A general collection of static utility methods
- */
-public class QUtil {
-    
-    public static boolean debugEnabled = true;
-
-    /**
-     * Generates a new secure space public/private keypair
-     * @return an array of 2 strings, first one is SSK Public Key, second one
-     * is SSK Private Key.
-     */
-    public static String [] newKeys() {
-        Object [] keypair = I2PAppContext.getGlobalContext().keyGenerator().generateSigningKeypair();
-        SigningPublicKey pub = (SigningPublicKey)keypair[0];
-        SigningPrivateKey priv = (SigningPrivateKey)keypair[1];
-        String [] sskKeypair = new String[2];
-        sskKeypair[0] = hashPubKey(pub);
-        sskKeypair[1] = priv.toBase64();
-        return sskKeypair;
-    }
-
-    /**
-     * converts a signed space private key (in base64)
-     * to its base64 ssk public equivalent
-     * @param priv64 SSK private key string as base64
-     * @return public key, base64-encoded
-     */
-    public static String privateToPubHash(String priv)
-        throws DataFormatException
-    {
-        return hashPubKey(new SigningPrivateKey(priv).toPublic());
-    }
-
-    public static SigningPublicKey privateToPublic(String priv64)
-        throws DataFormatException
-    {
-        SigningPrivateKey priv = new SigningPrivateKey(priv64);
-        SigningPublicKey pub = priv.toPublic();
-        return pub;
-    }
-
-    public static String hashPubKey(String pub64)
-        throws DataFormatException
-    {
-        return hashPubKey(new SigningPublicKey(pub64));
-    }
-
-    /**
-     * hashes a public key for use in signed space keypairs
-     * possibly shorten this
-     */
-    public static String hashPubKey(SigningPublicKey pub) {
-        String hashed = sha64(pub.toByteArray());
-        String abbrev = hashed.substring(0, 24);
-        return abbrev;
-    }
-
-    /**
-     * returns base64 of sha hash of a string
-     */
-    public static String sha64(String raw) {
-        return sha64(raw.getBytes());
-    }
-
-    public static String sha64(byte [] raw) {
-        //return stripEquals(Base64.encode(sha(raw)));
-        return Base64.encode(sha(raw)).replaceAll("[=]", "");
-    }
-
-    public static byte [] sha(String raw) {
-        return sha(raw.getBytes());
-    }
-
-    public static byte [] sha(byte [] raw) {
-        return I2PAppContext.getGlobalContext().sha().calculateHash(raw).getData();
-    }
-
-    public static void debug(String s) {
-        if (debugEnabled) {
-            System.out.println("QSSL:"+s);
-        }
-    }
-
-}
diff --git a/apps/q/java/src/net/i2p/aum/q/QWorkerThread.java b/apps/q/java/src/net/i2p/aum/q/QWorkerThread.java
deleted file mode 100644
index 502de342e874c523b6bba819dea4d0d737a27d9c..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/q/QWorkerThread.java
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * QWorkerThread.java
- *
- * Created on April 17, 2005, 2:44 PM
- */
-
-package net.i2p.aum.q;
-
-import java.io.File;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Vector;
-
-import net.i2p.aum.SimpleFile;
-
-/**
- * Thread which performs a single background job for a nod
- */
-
-class QWorkerThread extends Thread {
-
-    QNode node;
-    Hashtable job;
-    String jobTime;
-    String peerId;
-    String jobDesc;
-    
-    /*
-     * Creates this thread for executing a background job for the node
-     * @param node the node for which this job is to run
-     * @param jobTime unixtime-milliseconds at which job is to run,
-     * represented as string because it denotes a file in the node's jobs dir
-     */
-    public QWorkerThread(QNode node, String jobTime) {
-        this.node = node;
-        this.jobTime = jobTime;
-    }
-    
-    public void run() {
-        try {
-            node.log.info("worker: executing job: "+jobTime);
-
-            // reconstitute the job from its serialisation in jobs directory
-            job = node.loadJob(jobTime);
-            jobDesc = node.loadJobDescription(jobTime);
-
-            // a couple of details
-            String cmd = (String)job.get("cmd");
-            peerId = (String)job.get("peerId");
-
-            // dispatch off to required handler routine
-            if (cmd.equals("getUpdate")) {
-                doGetUpdate();
-            }
-            else if (cmd.equals("hello")) {
-                doHello();
-            }
-            else if (cmd.equals("localPutItem")) {
-                doLocalPutItem();
-            }
-            else if (cmd.equals("uploadItem")) {
-                doUploadItem();
-            }
-            else if (cmd.equals("test")) {
-                doTest();
-            }
-            else if (cmd.equals("shutdown")) {
-                doShutdown();
-            }
-            else {
-                node.log.error("workerthread.run: unrecognised command '"+cmd+"'");
-                System.out.println("workerthread.run: unrecognised command '"+cmd+"'");
-            }
-            
-        } catch (Exception e) {
-            e.printStackTrace();
-            node.log.warn("worker thread crashed");
-        }
-        
-        // finished (or failed), so replenish the jobs pool
-        node.threadPool.release();
-        
-        // and remove the job record and description
-        try {
-            new File(node.jobsDir + node.sep + jobTime).delete();
-            new File(node.jobsDir + node.sep + jobTime + ".desc").delete();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-    
-    public void doTest() throws Exception {
-        
-        String msg = (String)job.get("msg");
-        System.out.println("TESTJOB: msg='"+msg+"'");
-    }
-    
-    public void doShutdown() throws Exception {
-        
-        try {
-            new File(node.jobsDir + node.sep + jobTime).delete();
-            new File(node.jobsDir + node.sep + jobTime + ".desc").delete();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-        SimpleFile f = new SimpleFile("/tmp/eeee", "rws");
-        f.write("xxx");
-        node.isRunning = false;
-        Runtime.getRuntime().halt(0);
-    }
-    
-    public void doLocalPutItem() throws Exception {
-        Hashtable metadata = (Hashtable)job.get("metadata");
-        String path = (String)job.get("localDataFilePath");
-        SimpleFile f = new SimpleFile(path, "r");
-        byte [] data = f.readBytes();
-        
-        System.out.println("doLocalPutItem: path='"+path+"' dataLen="+data.length+" metadata="+metadata);
-        node.putItem(metadata, data);
-    }
-    
-    /**
-     * <p>Upload a locally-inserted data item to n remote hubs.</p>
-     * <p>This is one intricate algorithm. The aim is to upload the content
-     *    item to the 3 peers which are closest (Kademlia-wise) to the item's URI.
-     *    Some requirements include:
-     *    <ul>
-     *     <li>If we discover new peers over time, we have to consider these peers
-     *         as upload targets</li>
-     *     <li>If upload to an individual peer fails, we have to retry a few times</li>
-     *     <li>If there aren't enough viable peers yet, we need to keep rescheduling this
-     *         job till enough peers come online</li>
-     *     <li>Don't hog a thread slot on the jobs queue, give other jobs a chance to run</li>
-     *    </ul>
-     * </p>
-     *
-     */
-    public void doUploadItem() throws QException {
-        QDataItem item = (QDataItem)job.get("item");
-        String uri = (String)item.get("uri");
-        String desc = "uploadItem:uri="+uri;
-        byte [] data = item._data;
-        
-        Hashtable peersUploaded = (Hashtable)job.get("peersUploaded");
-        Hashtable peersPending = (Hashtable)job.get("peersPending");
-        Hashtable peersFailed = (Hashtable)job.get("peersFailed");
-        Hashtable peersNumTries = (Hashtable)job.get("peersNumTries");
-        
-        String itemHash = item.getStoreFilename();
-        QPeer peerRec;
-        
-        // get current list of up to 100 closest peers to item's URI
-        Vector cPeers = node.peersClosestTo(uri, 100);
-
-        // loop on this list, try to upload item to n of them
-        for (Enumeration en = cPeers.elements(); en.hasMoreElements();) {
-            QPeer peer = (QPeer)en.nextElement();
-            String peerId = peer.getId();
-            
-            // skip this peer if we've already succeeded or failed with it
-            if (peersFailed.containsKey(peerId) || peersUploaded.containsKey(peerId)) {
-                continue;
-            }
-            
-            // if there are less than 3 or more pending peers, add this peer to
-            // pending list, otherwise skip it
-            if (!peersPending.containsKey(peerId)) {
-                if (peersPending.size() < 3) {
-                    peersPending.put(peerId, "");
-                } else {
-                    continue;
-                }
-            }
-
-            // try to insert item to this peer
-            boolean uploadedOk;
-            try {
-                Hashtable res = node.peerPutItem(peerId, item, item._data);
-                if (res.containsKey("status") && ((String)res.get("status")).equals("ok")) {
-                    // successful upload
-                    uploadedOk = true;
-                } else {
-                    // upload failed for some reason
-                    uploadedOk = false;
-                    System.out.println("upload failure:"+res);
-                }
-            } catch (Exception e) {
-                // possibly because peer is offline or presently unreachable
-                uploadedOk = false;
-                e.printStackTrace();
-                System.out.println("upload failure");
-            }
-            
-            // how'd the upload go?
-            if (uploadedOk) {
-                // successful - remove from pending list, add to success list
-                peersPending.remove(peerId);
-                peersNumTries.remove(peerId);
-                peersUploaded.put(peerId, "");
-                
-                // have we successfully uploaded to 3 or more peers yet?
-                if (peersUploaded.size() >= 3) {
-                    // yep, this job has now run its course and can expire
-                    return;
-                } else {
-                    // bust out so we don't hog a scheduler slot
-                    node.runAfter(5000, job, desc);
-                    return;
-                }
-                
-            } else {
-                // insert failed
-                // increment retry count, fail this peer if retries maxed out
-                int numTries = ((Integer)peersNumTries.get(peerId)).intValue() + 1;
-                if (numTries > 4) {
-                    // move peer from pending list to failed list
-                    peersPending.remove(peerId);
-                    peersNumTries.remove(peerId);
-                    peersFailed.put(peerId, "");
-                }
-                
-                // bust out so we don't hog a scheduler slot
-                node.runAfter(30000, job, desc);
-                return;
-            }
-        }
-        
-        // we'return out of peers, reschedule this job to retry in an hour's time
-        node.runAfter(3600000, job, desc);
-    }
-    
-    public void doHello() {
-        QPeer peerRec = (QPeer)node.peers.get(peerId);
-        
-        node.log.debug("doHello: "+node.id+" -> "+peerId);
-        
-        try {
-            // execute peers list req on peer
-            Hashtable result = node.peerHello(peerId, node.destStr);
-            
-            // see what happened
-            String status = (String)result.get("status");
-            if (status.equals("ok")) {
-                peerRec.markAsGreeted();
-                
-                // and, schedule in regular peersList updates
-                node.schedulePeerUpdateJob(peerRec);
-            }
-        } catch (Exception e) {
-            node.log.warn("Got an xmlrpc client failure, trying again in 1 hour", e);
-            
-            // schedule another attempt in 2 hours
-            Hashtable job = new Hashtable();
-            job.put("cmd", "hello");
-            job.put("peerId", peerId);
-            node.runAfter(3600000, job, "hello:peerId="+peerId);
-        }
-    }
-    
-    public void doGetUpdate() {
-        QPeer peerRec = (QPeer)node.peers.get(peerId);
-        int timeLastPeersUpdate = peerRec.getTimeLastUpdate();
-        int timeNextContact;
-        int doCatalog = ((Integer)(job.get("includeCatalog"))).intValue();
-        int doPeers = ((Integer)(job.get("includePeers"))).intValue();
-        Vector peers;
-        Vector items;
-        
-        node.log.info("doGetUpdate: "+node.id+" -> "+peerId);
-
-        try {
-            // execute peers list req on peer
-            Hashtable result = node.peerGetUpdate(
-            peerId, timeLastPeersUpdate, doPeers, doCatalog);
-            
-            // see what happened
-            String status = (String)result.get("status");
-            if (status.equals("ok")) {
-                
-                node.log.debug("doGetUpdate: successful, result="+result);
-                
-                int i;
-                
-                // success - add all new peers
-                peers = (Vector)result.get("peers");
-                int npeers = peers.size();
-                for (i=0; i<npeers; i++) {
-                    String destStr = (String)peers.get(i);
-                    String destId = node.destToId(destStr);
-                    // test if this is a new dest
-                    if (!node.peers.containsKey(destId)) {
-                        node.log.debug("doGetPeerList: adding new peer "+destId);
-                        node.newPeer(destStr);
-                    }
-                    else {
-                        node.log.debug("doGetPeerList: we already know peer "+destId);
-                    }
-                }
-                
-                // also add all new items
-                items = (Vector)result.get("items");
-                int nitems = items.size();
-                for (i=0; i<nitems; i++) {
-                    Hashtable metadata = (Hashtable)(items.get(i));
-                    node.addMetadataToCatalog(metadata);
-                    String key = (String)metadata.get("key");
-                    node.addItemLocation(key, peerId);
-                }
-                
-                // get time of next update
-                int nextTime = ((Integer)result.get("timeNextContact")).intValue();
-                int lastTime = ((Integer)result.get("timeUpdateEnds")).intValue();
-                peerRec.setTimeNextContact(nextTime);
-                peerRec.setTimeLastUpdate(lastTime);
-                
-                //System.out.println("doGetUpdate: timeNextContact="+nextTime);
-                
-                // schedule another job at recommended time
-                Hashtable job = new Hashtable();
-                job.put("cmd", "getUpdate");
-                job.put("peerId", peerId);
-                job.put("includePeers", new Integer(doPeers));
-                job.put("includeCatalog", new Integer(doCatalog));
-                node.runAt(((long)nextTime)*1000, job, "getUpdate:peerId="+peerId);
-            }
-        } catch (Exception e) {
-            node.log.warn("xmlrpc client failure, rescheduling 180 secs from now", e);
-            
-            // schedule another attempt in 30 secs
-            Hashtable job = new Hashtable();
-            job.put("cmd", "getUpdate");
-            job.put("peerId", peerId);
-            job.put("includePeers", new Integer(doPeers));
-            job.put("includeCatalog", new Integer(doCatalog));
-            node.runAfter(180000, job, "getUpdate:peerId="+peerId);
-        }
-        
-        //peerRec.setProperty("timeLastPeersUpdate", "0");
-        //peerRec.setProperty("timeLastContentUpdate", "0");
-        //peerRec.setProperty("timeLastContact", "0");
-        //peerRec.setProperty("timeNextContact", "0");
-        
-    }
-}
-    
-
diff --git a/apps/q/java/src/net/i2p/aum/test/HttpServerTest.java b/apps/q/java/src/net/i2p/aum/test/HttpServerTest.java
deleted file mode 100644
index 797982d97f01b531432100bff1cd576c20a7a6a1..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/test/HttpServerTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * HttpServerTest.java
- * Created on April 9, 2005, 2:59 AM
- */
-package net.i2p.aum.test;
-
-import net.i2p.aum.http.MiniHttpRequestPage;
-import net.i2p.aum.http.MiniHttpServer;
-
-public class HttpServerTest extends MiniHttpRequestPage {
-    
-    public HttpServerTest(MiniHttpServer serv, Object socket) throws Exception {
-        super(serv, socket);
-    }
-
-    public void on_GET() throws Exception {
-
-        setContentType("text/html");
-        setServer("aum's MiniHttpServer demo default - GET");
-        head.nest("title").raw("aum's MiniHttpServer demo - GET");
-        body.nest("h1")
-            .raw("aum MiniHttpServer demo - GET");
-        body.raw("You requested: "+reqFile)
-            .br().br()
-            .nest("form action=foo.html method=POST")
-                .add("input type=submit name=doit value=doit")
-                .br()
-                .add("input type=text name=fred value=blah")
-                .br()
-                .nest("textarea name=mary")
-                    .raw("red green");
-        body.br()
-            .add(dumpVars());
-    }
-
-    public void on_POST() {
-        setContentType("text/html");
-        setServer("aum's MiniHttpServer demo default - POST");
-
-        head.nest("title").raw("aum's MiniHttpServer demo - POST");
-        body.nest("h1").raw("aum MiniHttpServer demo - POST");
-        body.raw("You requested: "+reqFile)
-            .br().br()
-            .nest("form action=foo.html method=POST")
-                .add("input type=submit name=doit value=doit")
-                .br()
-                .add("input type=text name=fred value=blah")
-                .br()
-                .nest("textarea name=mary")
-                    .raw("red green");
-        body.br()
-            .add(dumpVars());
-    }
-
-    public static void main(String[] args) {
-        MiniHttpServer serv = new MiniHttpServer(HttpServerTest.class, 18000);
-        serv.run();
-    }
-}
diff --git a/apps/q/java/src/net/i2p/aum/util/Ico2Java.java b/apps/q/java/src/net/i2p/aum/util/Ico2Java.java
deleted file mode 100644
index fa6fe9c735bebc9c201604a93aca555bdc9babf1..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/aum/util/Ico2Java.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package net.i2p.aum.util;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileWriter;
-import java.io.InputStream;
-
-/**
- * development utility - converts Favicon.ico to
- * a java source file net.i2p.aum.q.Favicon.java, containing
- * the favicon image
- */
-public class Ico2Java {
-
-    public static void convertToJava(String name) throws Exception {
-
-        File f = new File(name);
-
-        byte [] image = new byte[(int)f.length()];
-        InputStream fi = new FileInputStream(f);
-        fi.read(image);
-
-        // ok, now generate a java file
-        String basename = name.substring(0, name.length()-4);
-        String jName = "src/net/i2p/aum/q/" + basename + ".java";
-        FileWriter fo = new FileWriter(jName);
-        fo.write("package net.i2p.aum.q;\n");
-        fo.write("public class "+basename+" {\n");
-        fo.write("    public static byte [] image = {");
-        for (int i=0; i<image.length; i++) {
-            if (i % 16 == 0) {
-                fo.write("\n        ");
-            }
-            fo.write(String.valueOf(image[i])+", ");
-        }
-        
-        fo.write("\n        };\n");
-        fo.write("}\n");
-        fo.close();
-    }
-
-    public static void main(String [] args) {
-        if (args.length != 1) {
-            System.out.println("Usage: ico2java filename.ico");
-            System.exit(1);
-        }
-
-        File f = new File(args[0]);
-        if (!f.isFile()) {
-            System.out.println("No such file '"+args[0]+"'");
-            System.exit(1);
-        }
-
-        try {
-            convertToJava(args[0]);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-}
-
diff --git a/apps/q/java/src/net/i2p/i2ptunnel/I2PTunnelXMLWrapper.java b/apps/q/java/src/net/i2p/i2ptunnel/I2PTunnelXMLWrapper.java
deleted file mode 100644
index 725ef67e64af9b59dd5b2e106df3ffb2a3bd065c..0000000000000000000000000000000000000000
--- a/apps/q/java/src/net/i2p/i2ptunnel/I2PTunnelXMLWrapper.java
+++ /dev/null
@@ -1,406 +0,0 @@
-package net.i2p.i2ptunnel;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.lang.reflect.Array;
-import java.util.Hashtable;
-import java.util.Vector;
-
-
-/**
- * subclasses I2PTunnel, adding some methods to facilitate
- * the I2PTunnel XML-RPC server. We have to put this class
- * into net.i2p.i2ptunnel, because there are some non-public
- * methods we need to access. For doco on the methods in
- * this class refer to the corresponding doco in I2PTunnelXMLObject
- */
-public class I2PTunnelXMLWrapper extends I2PTunnel
-{
-    public Hashtable xmlrpcLookup(String hostname)
-    {
-        String cmdOut;
-        String [] args;
-        BufferLogger log = new BufferLogger();
-        Hashtable result = new Hashtable();
-    
-        System.out.println("xmlrpcLookup: hostname='" + hostname + "'");
-    
-        args = new String[1];
-        args[0] = hostname;
-    
-        //System.out.println("Invoking runLookup");    
-    
-        runLookup(args, log);
-        //System.out.println("Back from runLookup");
-    
-        cmdOut = log.getBuffer().trim();
-        //System.out.println("cmdOut=" + cmdOut);
-    
-        if (cmdOut.equals("Unknown host"))
-        {
-            result.put("status", "fail");
-        }
-        else
-        {
-            result.put("status", "ok");
-            result.put("dest", cmdOut);
-        }
-    
-        return result;
-    }
-    
-    public Hashtable xmlrpcList()
-    {
-        Hashtable result = new Hashtable();
-        BufferLogger log = new BufferLogger();
-        Vector jobs = new Vector();
-    
-        // run the listing
-        runList(log);
-    
-        String raw = log.getBuffer();
-        //System.out.println("list: raw='"+raw+"'");
-        if (raw.equals(""))
-        {
-            System.out.println("list: no jobs");
-        }
-        else
-        {
-            // got 1 or more job lines, parse them and assemble into jobs array
-            String [] rawlines = raw.trim().split("\\n+");
-        
-            int numLines = Array.getLength(rawlines);
-            int i;
-        
-            System.out.println("list: numLines="+numLines);
-        
-            for (i = 0; i < numLines; i++)
-            {
-                String line = rawlines[i];
-        
-                System.out.println("list: line["+i+"]="+line);
-        
-                // carve up each job line into constituent fields
-                String [] flds = line.split("\\s+");
-                
-                String jobFld = flds[0];
-                String tcpFld = flds[1];
-                String typeFld = flds[2];
-                String i2pFld = flds[3];
-        
-                Integer portInt;
-                int port;
-        
-                Hashtable job = new Hashtable();
-        
-                // stick in jobnumber, as int
-                Integer jobNumInt = new Integer(jobFld.substring(1, jobFld.length()-1));
-                int jobNum = jobNumInt.intValue();
-                job.put("job", jobNumInt);
-        
-                // rest is type-dependent
-                if (typeFld.equals("<-"))
-                {
-                    //System.out.println("server");
-        
-                    // fill out dict for a 'server' result
-                    job.put("type", "server");
-        
-                    // tcp side is in form "hostname/ipaddr:port", carve up
-                    String hostsStr;
-                    String hostStr;
-                    String ipStr;
-                    String portStr;
-                    
-                    String [] tmpFlds = tcpFld.split(":");
-        
-                    //System.out.println("tmpFlds="+tmpFlds);
-        
-                    hostsStr = tmpFlds[0];
-                    portStr = tmpFlds[1];
-        
-                    //System.out.println("hostsStr="+hostsStr+" portStr="+portStr);
-        
-                    portInt = new Integer(portStr);
-                    port = portInt.intValue();
-        
-                    tmpFlds = hostsStr.split("/");
-                    hostStr = tmpFlds[0];
-                    ipStr = tmpFlds[1];
-        
-                    //System.out.println("hostStr="+hostStr+" ipStr="+ipStr);
-        
-                    job.put("host", hostStr);
-                    job.put("ip", ipStr);
-                    job.put("port", portInt);
-                }
-                else
-                {
-                    //System.out.println("client");
-        
-                    // fill out dict for a 'client' result
-                    job.put("type", "client");
-        
-                    portInt = new Integer(tcpFld);
-                    port = portInt.intValue();
-        
-                    job.put("dest", i2pFld);
-                    job.put("port", portInt);
-                }
-        
-                jobs.add(job);
-            }
-        }
-    
-        result.put("status", "ok");
-        //result.put("rawlist", raw);
-        //result.put("rawlines", rawlines);
-        result.put("jobs", jobs);
-    
-        return result;
-    }
-    
-    public Hashtable xmlrpcClient(int portNum, String dest)
-    {
-        Hashtable result = new Hashtable();
-        BufferLogger log = new BufferLogger();
-    
-        String [] args = new String[2];
-    
-        Integer portNumInt = new Integer(portNum);
-        args[0] = portNumInt.toString();
-        args[1] = dest;
-        runClient(args, log);
-    
-        String reply = log.getBuffer();
-    
-        result.put("status", "ok");
-        result.put("result", reply);
-    
-        return result;
-    }
-    
-    public Hashtable xmlrpcServer(String host, int port, String key)
-    {
-        Hashtable result = new Hashtable();
-        BufferLogger log = new BufferLogger();
-    
-        File keyBinFile;
-    
-        // create array for cli args
-        String [] args = new String[3];
-    
-        // arg0 is host - easy
-        args[0] = host;
-    
-        // arg1 is port, convert from int to string
-        Integer portInt = new Integer(port);
-        args[1] = portInt.toString();
-    
-        // arg2 is key filename
-        // gotta convert base64 string to bin, write to
-        // a temporary file, and extract the file path
-        String keyBin = new String(net.i2p.data.Base64.decode(key));
-        try
-        {
-            keyBinFile = File.createTempFile("xmlrpc_", ".priv");
-            keyBinFile.createNewFile();
-            FileWriter keyBinFileWriter = new FileWriter(keyBinFile);
-            keyBinFileWriter.write(keyBin, 0, keyBin.length());
-            keyBinFileWriter.flush();
-            keyBinFileWriter.close();
-        }
-        catch (IOException e)
-        {
-            result.put("status", "fail");
-            result.put("error", "IOException");
-            return result;
-        }
-        args[2] = keyBinFile.getAbsolutePath();
-    
-        // do the run cmd
-        runServer(args, log);
-    
-        // finished with privkey bin file, delete it
-        keyBinFile.delete();
-        
-        String reply = log.getBuffer().trim();
-        
-        // Replies with 'Ready!' if ok, something else if not
-        if (reply.equals("Ready!"))
-        {
-            result.put("status", "ok");
-        }
-        else
-        {
-            result.put("status", "fail");
-            result.put("error", reply);
-        }
-    
-        return result;
-    }
-    
-    //
-    // generate a new keypair
-    //
-    
-    public Hashtable xmlrpcGenkeys()
-    {
-        Hashtable result = new Hashtable();
-        BufferLogger log = new BufferLogger();
-        String reply = "";
-    
-        // need a couple of output streams to collect the keys
-        ByteArrayOutputStream privStream = new ByteArrayOutputStream();
-        ByteArrayOutputStream destStream = new ByteArrayOutputStream();
-    
-        // generate the keys
-        makeKey(privStream, destStream, log);
-    
-        // extract keys from output streams and convert into base64 strings
-        String priv64 = net.i2p.data.Base64.encode(privStream.toByteArray());
-        String dest64 = net.i2p.data.Base64.encode(destStream.toByteArray());
-    
-        // build up result map and bail
-        result.put("status", "ok");
-        result.put("priv", priv64);
-        result.put("dest", dest64);
-    
-        return result;
-    }
-    
-    // various front-ends for close
-    
-    public Hashtable xmlrpcClose(int jobnum)
-    {
-        Hashtable result = new Hashtable();
-        BufferLogger log = new BufferLogger();
-        String [] args = new String[1];
-    
-        String jobStr = String.valueOf(jobnum);
-        args[0] = jobStr;
-    
-        runClose(args, log);
-    
-        String logOut = log.getBuffer();
-    
-        //System.out.println("close(int): got "+logOut);
-    
-        result.put("status", "ok");
-        return result;
-    }
-    
-    public Hashtable xmlrpcClose(Hashtable criteria)
-    {
-        Hashtable result = new Hashtable();
-        BufferLogger log = new BufferLogger();
-        String [] args = new String[1];
-    
-        // try special case, job==-1 means close all jobs
-        if (criteria.containsKey("job"))
-        {
-            Object j = criteria.get("job");
-            //System.out.println("job="+j);
-        }
-            
-    
-        if (criteria.containsKey("job") && criteria.get("job").equals("all"))
-        {
-            // easy - just fire a 'close all'
-            //System.out.println("close: job=all");
-            return xmlrpcClose("all");
-        }
-    
-        // harder - get joblist and match criteria
-        // get a list of running jobs
-        Hashtable jobs = xmlrpcList();
-        Vector jobsList = (Vector)(jobs.get("jobs"));
-        //int numJobs = Array.length(jobsList);
-        int numJobs = jobsList.size();
-    
-        int i;
-        for (i = 0; i < numJobs; i++)
-        {
-            boolean closing = false;
-        
-            // fetch each job, and test if it matches our criteria
-            Hashtable job = (Hashtable)jobsList.get(i);
-            //System.out.println("close: existing job: "+job);
-    
-            if (criteria.containsKey("job") && criteria.get("job").equals(job.get("job")))
-            {
-                //System.out.println("close: matched jobnum");
-                closing = true;
-            }
-            if (criteria.containsKey("type") && criteria.get("type").equals(job.get("type")))
-            {
-                //System.out.println("close: matched type");
-                closing = true;
-            }
-            if (criteria.containsKey("port") && criteria.get("port").equals(job.get("port")))
-            {
-                //System.out.println("close: matched port");
-                closing = true;
-            }
-            if (criteria.containsKey("host") && job.containsKey("host")
-                && (criteria.get("host").equals(job.get("host"))))
-            {
-                //System.out.println("close: matched host="+criteria.get("host"));
-                closing = true;
-            }
-            if (criteria.containsKey("ip") && job.containsKey("ip")
-                && (criteria.get("ip").equals(job.get("ip"))))
-            {
-                //System.out.println("close: matched ip="+criteria.get("ip"));
-                closing = true;
-            }
-    
-            if (closing)
-            {
-                String jobStr = String.valueOf(job.get("job"));
-                //System.out.println("close: matched criteria - closing job "+jobStr);
-                args[0] = jobStr;
-                runClose(args, log);
-                result.put("status", "ok");
-                return result;
-            }
-        }
-    
-        result.put("status", "ok");
-        return result;
-    }
-    
-    public Hashtable xmlrpcClose(String job)
-    {
-        Hashtable result = new Hashtable();
-        BufferLogger log = new BufferLogger();
-        String [] args = new String[1];
-    
-        // easy 'close all' case
-        if (job.equals("all"))
-        {
-            args[0] = "all";
-            runClose(args, log);
-            result.put("status", "ok");
-            return result;
-        }
-    
-        // try to convert job to an int
-        try
-        {
-            int jobnum = new Integer(job).intValue();
-            return xmlrpcClose(jobnum);
-        }
-        catch (NumberFormatException e)
-        {
-            result.put("status", "error");
-            result.put("error", "cannot convert job number arg to an int");
-            return result;
-        }
-    }
-    
-}
-
-
diff --git a/apps/q/java/web.xml b/apps/q/java/web.xml
deleted file mode 100644
index ede6dabf979590c1d2ebe2406c444c068158973c..0000000000000000000000000000000000000000
--- a/apps/q/java/web.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE web-app
-    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
-    "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
-
-<web-app>
-  <display-name>QConsole</display-name>
-
-  <servlet>
-    <servlet-name>QConsole</servlet-name>
-    <servlet-class>net.i2p.aum.q.QConsole</servlet-class>
-    <load-on-startup>1</load-on-startup>
-  </servlet>
-
-  <servlet-mapping>
-    <servlet-name>QConsole</servlet-name>
-    <url-pattern>/*</url-pattern>
-  </servlet-mapping>
-
-</web-app>
-
diff --git a/apps/q/java/xmlrpc.jar b/apps/q/java/xmlrpc.jar
deleted file mode 100644
index 6e3d8bdee110f5fd269a1701a6162a7df92fbd4f..0000000000000000000000000000000000000000
Binary files a/apps/q/java/xmlrpc.jar and /dev/null differ
diff --git a/apps/rome/readme.txt b/apps/rome/readme.txt
deleted file mode 100644
index 412710a2dcaa7daef76f18173555c0316adcf5d0..0000000000000000000000000000000000000000
--- a/apps/rome/readme.txt
+++ /dev/null
@@ -1 +0,0 @@
-This is ROME 0.8 from http://rome.dev.java.net/, released under a BSD license
diff --git a/apps/rome/rome-0.8.jar b/apps/rome/rome-0.8.jar
deleted file mode 100644
index 27d2ad3293ab60efdfbc073ef2a3ae88d3e95d75..0000000000000000000000000000000000000000
Binary files a/apps/rome/rome-0.8.jar and /dev/null differ
diff --git a/apps/stasher/python/README.txt b/apps/stasher/python/README.txt
deleted file mode 100644
index 7c2a4d0ab8f7fcb3470324effaa1e4f42b2ab10e..0000000000000000000000000000000000000000
--- a/apps/stasher/python/README.txt
+++ /dev/null
@@ -1,77 +0,0 @@
-STASHER README
-
------------------------
-INSTALLING STASHER
-
-Prerequisite:
-
-Before you can install/run Stasher, you will first need to have installed
-the I2P Python modules - available in cvs at i2p/apps/sam/python.
-
-To install stasher, just make sure you've got the latest cvs, then type
-  python setup.py install
-as root.
-
-This installs the stasher engine, plus a wrapper client script called
-'stasher', which setup.py will install into your execution path.
-
-If you don't like the thought of becoming root, you could just put stasher.py
-on your execution path, and/or create a symlink called 'stasher'.
-
-Test your installation by typing 'stasher -h' - this should display
-a help message.
-
-------------------------
-DOZE USERS PLEASE NOTE
-
-You'll need to watch and see where the stasher.py
-wrapper script gets installed. On my box, it ends up on d:\python23\scripts,
-but on your box it'll likely go into c:\python23\scripts.
-
-You may either update your system PATH environment variable to include your
-python scripts directory, OR, you can copy stasher.py to anywhere that's
-on your path.
-
-In the explanations below, note that wherever I say to type 'stasher', you'll
-need to type 'stasher.py' instead.
-
-------------------------
-WARNING
-
-This is a very early pre-alpha test version of stasher.
-It is only capable of storing or retrieving files of
-less than 29k in size.
-
-Also, files are totally insecure - anyone can overwrite any keys you
-insert, and vice versa.
-
-I'll be adding support for CHK-like and SSK-like keys in due course.
-
-------------------------
-USING STASHER
-
-To see stasher's options, type:
-
-  stasher -h
-
-This should dump out a verbose help message.
-
-To start a stasher node, type:
-
-  stasher start
-
-To shut down a stasher node, type:
-
-  stasher stop
-
-To insert a file into stasher, type:
-
-  stasher put mykey myfile
-
-Note, if you don't supply a filename, stasher can read
-the file from standard input.
-
-To retrieve a file from stasher, type:
-
-  stasher get mykey
-
diff --git a/apps/stasher/python/noderefs/aum.stasher b/apps/stasher/python/noderefs/aum.stasher
deleted file mode 100644
index 5692d6019c7808d238e705e1ee7f2592633d1410..0000000000000000000000000000000000000000
--- a/apps/stasher/python/noderefs/aum.stasher
+++ /dev/null
@@ -1 +0,0 @@
-qeu89U8BXS8~jlsGvr-wjMvSIcXpYZ6wX2iEmGFI2Lm9eMV-yZfoZcAAF1Ll8Ck3FvIkH3~N0OobuGjcVZTiZ5PC2~h-zGHVaBPbsnOdVjYEeCGUwxwlNW6cxZZ6SfWbjTxXrpbSjLYZtlnGTBm5cd2Qaj61~A4lcoI72kj-v9GNXD5zeCQ9PeqKJHRN5p29VR8lTh9eqoIIHlGnsllQeZieeFJEAdnydOTi1ERzm4Hftq0P47lWu2FYh3aMtxI7HLeklWrmnQ--rW0XJ~xndWl45e~DEDaIL0k2FfMEmWYPtF-8l-xBX9IwIx8uZ2tcsexxLvJCY8-RiI4wgBqSf1CxPTGJ4TYUIqTUcMv2Sku8WslAdrWSPJofHWmeAmTJdSgCe8ZwvgMLkNZVeGgEccwtCDJbe5AeyJaQDFOTLlgwhHu5ExbyUPZtNZ4nSg-~qiGnpFTdgonqEYoJF9LvSaAgSfhOS3kdtZ6kKqgMFFY8InpGgCBuc6A6c5hsfCVjAAAA
\ No newline at end of file
diff --git a/apps/stasher/python/scripts/stasher b/apps/stasher/python/scripts/stasher
deleted file mode 100644
index 601cfee4ed1800a9502e933b3f46e388b030427d..0000000000000000000000000000000000000000
--- a/apps/stasher/python/scripts/stasher
+++ /dev/null
@@ -1,10 +0,0 @@
-#! /usr/bin/env python
-# wrapper script to run stasher node
-
-# set this to the directory where you've installed stasher
-stasherDir = "/path/to/my/stasher/dir"
-
-import sys
-sys.path.append(stasherDir)
-import stasher
-stasher.main()
diff --git a/apps/stasher/python/scripts/stasher.py b/apps/stasher/python/scripts/stasher.py
deleted file mode 100644
index 3f404e51199c0d0abdc56c04380f340e07945649..0000000000000000000000000000000000000000
--- a/apps/stasher/python/scripts/stasher.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# wrapper script to run stasher node
-
-# set this to the directory where you've installed stasher
-stasherDir = "/path/to/my/stasher/dir"
-
-import sys
-sys.path.append(stasherDir)
-import stasher
-stasher.main()
diff --git a/apps/stasher/python/setup.py b/apps/stasher/python/setup.py
deleted file mode 100644
index c81520c1f29c32527937b7df6ce6f854505e772d..0000000000000000000000000000000000000000
--- a/apps/stasher/python/setup.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#! /usr/bin/env python
-#@+leo-ver=4
-#@+node:@file setup-stasher.py
-#@@first
-"""
-This is the installation script for Stasher, a distributed
-file storage framework for I2P.
-"""
-
-import sys, os
-from distutils.core import setup
-
-oldcwd = os.getcwd()
-os.chdir("src")
-
-if sys.platform == 'win32':
-    stasherScript = "..\\scripts\\stasher.py"
-else:
-    stasherScript = "../scripts/stasher"
-
-
-try:
-    import i2p
-    import i2p.socket
-    import i2p.select
-except:
-    print "Sorry, but you don't seem to have the core I2P"
-    print "python library modules installed."
-    print "If you're installing from cvs, please go to"
-    print "i2p/apps/sam/python, become root, and type:"
-    print "  python setup.py install"
-    print "Then, retry this installation."
-    sys.exit(1)
-
-setup(name="Stasher",
-      version="0.0",
-      description="Kademlia-based P2P distributed file storage app for I2P",
-      author="aum",
-      author_email="aum_i2p@hotmail.com",
-      url="http://stasher.i2p",
-      py_modules = ['stasher', 'bencode'],
-      scripts = [stasherScript],
-     )
-#@nonl
-#@-node:@file setup-stasher.py
-#@-leo
diff --git a/apps/stasher/python/src/bencode.py b/apps/stasher/python/src/bencode.py
deleted file mode 100644
index 93af40744a6365578f7aa4b1589a98c451fe49f4..0000000000000000000000000000000000000000
--- a/apps/stasher/python/src/bencode.py
+++ /dev/null
@@ -1,254 +0,0 @@
-# Written by Petru Paler
-# see LICENSE.txt for license information
-
-from types import IntType, LongType, StringType, ListType, TupleType, DictType
-import re
-from cStringIO import StringIO
-
-int_filter = re.compile('(0|-?[1-9][0-9]*)e')
-
-def decode_int(x, f):
-    m = int_filter.match(x, f)
-    if m is None:
-        raise ValueError
-    return (long(m.group(1)), m.end())
-
-string_filter = re.compile('(0|[1-9][0-9]*):')
-
-def decode_string(x, f):
-    m = string_filter.match(x, f)
-    if m is None:
-        raise ValueError
-    l = int(m.group(1))
-    s = m.end()
-    return (x[s:s+l], s + l)
-
-def decode_list(x, f):
-    r = []
-    while x[f] != 'e':
-        v, f = bdecode_rec(x, f)
-        r.append(v)
-    return (r, f + 1)
-
-def decode_dict(x, f):
-    r = {}
-    lastkey = None
-    while x[f] != 'e':
-        k, f = decode_string(x, f)
-        if lastkey is not None and lastkey >= k:
-            raise ValueError
-        lastkey = k
-        v, f = bdecode_rec(x, f)
-        r[k] = v
-    return (r, f + 1)
-
-def bdecode_rec(x, f):
-    t = x[f]
-    if t == 'i':
-        return decode_int(x, f + 1)
-    elif t == 'l':
-        return decode_list(x, f + 1)
-    elif t == 'd':
-        return decode_dict(x, f + 1)
-    else:
-        return decode_string(x, f)
-
-def bdecode(x):
-    try:
-        r, l = bdecode_rec(x, 0)
-    except IndexError:
-        raise ValueError
-    if l != len(x):
-        raise ValueError
-    return r
-
-def test_bdecode():
-    try:
-        bdecode('0:0:')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('ie')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('i341foo382e')
-        assert 0
-    except ValueError:
-        pass
-    assert bdecode('i4e') == 4L
-    assert bdecode('i0e') == 0L
-    assert bdecode('i123456789e') == 123456789L
-    assert bdecode('i-10e') == -10L
-    try:
-        bdecode('i-0e')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('i123')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('i6easd')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('35208734823ljdahflajhdf')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('2:abfdjslhfld')
-        assert 0
-    except ValueError:
-        pass
-    assert bdecode('0:') == ''
-    assert bdecode('3:abc') == 'abc'
-    assert bdecode('10:1234567890') == '1234567890'
-    try:
-        bdecode('02:xy')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('l')
-        assert 0
-    except ValueError:
-        pass
-    assert bdecode('le') == []
-    try:
-        bdecode('leanfdldjfh')
-        assert 0
-    except ValueError:
-        pass
-    assert bdecode('l0:0:0:e') == ['', '', '']
-    try:
-        bdecode('relwjhrlewjh')
-        assert 0
-    except ValueError:
-        pass
-    assert bdecode('li1ei2ei3ee') == [1, 2, 3]
-    assert bdecode('l3:asd2:xye') == ['asd', 'xy']
-    assert bdecode('ll5:Alice3:Bobeli2ei3eee') == [['Alice', 'Bob'], [2, 3]]
-    try:
-        bdecode('d')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('defoobar')
-        assert 0
-    except ValueError:
-        pass
-    assert bdecode('de') == {}
-    assert bdecode('d3:agei25e4:eyes4:bluee') == {'age': 25, 'eyes': 'blue'}
-    assert bdecode('d8:spam.mp3d6:author5:Alice6:lengthi100000eee') == {'spam.mp3': {'author': 'Alice', 'length': 100000}}
-    try:
-        bdecode('d3:fooe')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('di1e0:e')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('d1:b0:1:a0:e')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('d1:a0:1:a0:e')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('i03e')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('l01:ae')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('9999:x')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('l0:')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('d0:0:')
-        assert 0
-    except ValueError:
-        pass
-    try:
-        bdecode('d0:')
-        assert 0
-    except ValueError:
-        pass
-
-def bencode_rec(x, b):
-    t = type(x)
-    if t in (IntType, LongType):
-        b.write('i%de' % x)
-    elif t is StringType:
-        b.write('%d:%s' % (len(x), x))
-    elif t in (ListType, TupleType):
-        b.write('l')
-        for e in x:
-            bencode_rec(e, b)
-        b.write('e')
-    elif t is DictType:
-        b.write('d')
-        keylist = x.keys()
-        keylist.sort()
-        for k in keylist:
-            assert type(k) is StringType
-            bencode_rec(k, b)
-            bencode_rec(x[k], b)
-        b.write('e')
-    else:
-        assert 0
-
-def bencode(x):
-    b = StringIO()
-    bencode_rec(x, b)
-    return b.getvalue()
-
-def test_bencode():
-    assert bencode(4) == 'i4e'
-    assert bencode(0) == 'i0e'
-    assert bencode(-10) == 'i-10e'
-    assert bencode(12345678901234567890L) == 'i12345678901234567890e'
-    assert bencode('') == '0:'
-    assert bencode('abc') == '3:abc'
-    assert bencode('1234567890') == '10:1234567890'
-    assert bencode([]) == 'le'
-    assert bencode([1, 2, 3]) == 'li1ei2ei3ee'
-    assert bencode([['Alice', 'Bob'], [2, 3]]) == 'll5:Alice3:Bobeli2ei3eee'
-    assert bencode({}) == 'de'
-    assert bencode({'age': 25, 'eyes': 'blue'}) == 'd3:agei25e4:eyes4:bluee'
-    assert bencode({'spam.mp3': {'author': 'Alice', 'length': 100000}}) == 'd8:spam.mp3d6:author5:Alice6:lengthi100000eee'
-    try:
-        bencode({1: 'foo'})
-        assert 0
-    except AssertionError:
-        pass
-
diff --git a/apps/stasher/python/src/code.leo b/apps/stasher/python/src/code.leo
deleted file mode 100644
index 4ae647bd2000cc3972cc68f056ae2f963c34709b..0000000000000000000000000000000000000000
--- a/apps/stasher/python/src/code.leo
+++ /dev/null
@@ -1,6339 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<leo_file>
-<leo_header file_format="2" tnodes="0" max_tnode_index="35" clone_windows="0"/>
-<globals body_outline_ratio="0.34387755102">
-	<global_window_position top="157" left="189" height="709" width="980"/>
-	<global_log_window_position top="0" left="0" height="0" width="0"/>
-</globals>
-<preferences>
-</preferences>
-<find_panel_settings>
-	<find_string></find_string>
-	<change_string></change_string>
-</find_panel_settings>
-<vnodes>
-<v t="aum.20040802153926" a="E"><vh>Kademlia</vh>
-<v t="aum.20040802154007" a="E" tnodeList="aum.20040802154007,aum.20040805131021.1,aum.20040802154945,aum.20040803013638,aum.20040803132528,aum.20040802154241,aum.20040811111244,aum.20040811111244.1,aum.20040807013038,aum.20040804122542,aum.20040813204308,aum.20040804122542.1,aum.20040804123117,aum.20040804123350,aum.20040804123526,aum.20040805123632,aum.20040804132551,aum.20040812152603,aum.20040807052750,aum.20040807053418,aum.20040807114327,aum.20040813204858,aum.20040804130347,aum.20040807013038.1,aum.20040807013038.2,aum.20040803005037,aum.20040803005037.1,aum.20040803005037.2,aum.20040803005037.3,aum.20040803005037.4,aum.20040803005309,aum.20040803134838,aum.20040803134838.1,aum.20040803010321,aum.20040803010321.1,aum.20040803134418,aum.20040803140601,aum.20040808133629,aum.20040808133941,aum.20040802154241.1,aum.20040802154241.2,aum.20040802164545,aum.20040808160207,aum.20040804001454,aum.20040813015858,aum.20040802165054,aum.20040803013434,aum.20040803013434.1,aum.20040803013638.1,aum.20040805182736,aum.20040802155814.2,aum.20040802155814.3,aum.20040802160335,aum.20040802160828,aum.20040802160828.1,aum.20040802160828.2,aum.20040805040409,aum.20040802184641,aum.20040804014643,aum.20040803143007,aum.20040803143007.1,aum.20040803143007.2,aum.20040804213616,aum.20040804220932,aum.20040807013038.3,aum.20040805012557,aum.20040805154232,aum.20040805012557.1,aum.20040806144708,aum.20040806144812,aum.20040806144829,aum.20040805001742,aum.20040805001926,aum.20040805004949,aum.20040805013630,aum.20040805001258,aum.20040805030707,aum.20040805185902,aum.20040805013903,aum.20040805013957,aum.20040807013038.4,aum.20040805014353,aum.20040805154253,aum.20040805014353.1,aum.20040805014900,aum.20040805014900.1,aum.20040805032351,aum.20040807013038.5,aum.20040806223556,aum.20040807033258,aum.20040807033258.1,aum.20040807033258.2,aum.20040807033818,aum.20040807033818.1,aum.20040807034729,aum.20040806223857,aum.20040807004327,aum.20040807004327.1,aum.20040807004327.5,aum.20040807004327.6,aum.20040807004327.11,aum.20040807004327.12,aum.20040811163127,aum.20040811221318,aum.20040807004327.13,aum.20040807004327.14,aum.20040807004327.15,aum.20040807004434,aum.20040807013835,aum.20040811235333,aum.20040807013944,aum.20040807004327.2,aum.20040807004327.3,aum.20040807004327.4,aum.20040807004327.7,aum.20040807004327.8,aum.20040807004327.9,aum.20040807004327.10,aum.20040807014538,aum.20040807044007,aum.20040805130159,aum.20040805143215,aum.20040805154306,aum.20040805130514,aum.20040805140416,aum.20040806234241,aum.20040807044007.1,aum.20040805185215,aum.20040806002319,aum.20040806132808,aum.20040805140632,aum.20040805141509,aum.20040811221628,aum.20040811222934,aum.20040807003220,aum.20040807013411,aum.20040813200853,aum.20040807013038.6,aum.20040805153146,aum.20040805154321,aum.20040808153427,aum.20040808163651,aum.20040816141128,aum.20040816141128.1,aum.20040816014757,aum.20040807013038.7,aum.20040805153315,aum.20040805154344,aum.20040808134739,aum.20040808134739.1,aum.20040816133040,aum.20040816135222,aum.20040816135222.1,aum.20040808140937,aum.20040808135302,aum.20040808140937.1,aum.20040808140937.2,aum.20040815164410,aum.20040815164410.1,aum.20040815164410.2,aum.20040815164410.3,aum.20040815164410.4,aum.20040815164410.7,aum.20040815164410.8,aum.20040815164410.5,aum.20040813232532,aum.20040813232532.1,aum.20040813233036,aum.20040813234214,aum.20040813232532.2,aum.20040813232532.3,aum.20040814004432,aum.20040814001156,aum.20040814001156.1,aum.20040814001912,aum.20040814001456,aum.20040814001522,aum.20040814001456.2,aum.20040814002236,aum.20040814103533,aum.20040814131117,aum.20040815170456,aum.20040814003559,aum.20040814002411,aum.20040814002411.1,aum.20040807013038.8,aum.20040802161601,aum.20040813204308.1,aum.20040802184641.1,aum.20040808203152,aum.20040805001449,aum.20040802161601.1,aum.20040802161601.2,aum.20040802161601.3,aum.20040802161601.4,aum.20040803015148,aum.20040802161601.5,aum.20040802161601.6,aum.20040803131111.2,aum.20040803201812.1,aum.20040815170327,aum.20040805140236,aum.20040805153555,aum.20040805153555.1,aum.20040803134102,aum.20040803131210,aum.20040803131210.1,aum.20040803131210.2,aum.20040803131210.3,aum.20040804205057,aum.20040804132053,aum.20040804200916,aum.20040803144052,aum.20040803163704,aum.20040804013647,aum.20040804014115.1,aum.20040808142950,aum.20040805040237,aum.20040803163704.1,aum.20040813234004,aum.20040813234004.1,aum.20040803142127,aum.20040803142127.1,aum.20040803142127.2,aum.20040803193605,aum.20040803200819,aum.20040804130722,aum.20040803132156,aum.20040814110540,aum.20040814110755,aum.20040805031135,aum.20040805031135.1,aum.20040808133629.1,aum.20040803132528.1,aum.20040810224601,aum.20040814112703,aum.20040814112703.1,aum.20040814120624,aum.20040813212609,aum.20040814015747,aum.20040813211551,aum.20040803141131,aum.20040812110124"><vh>@file stasher.py</vh>
-<v t="aum.20040805131021.1" a="M"><vh>explanatory comments</vh></v>
-<v t="aum.20040802154945"><vh>imports</vh></v>
-<v t="aum.20040803013638"><vh>constants</vh></v>
-<v t="aum.20040803132528"><vh>globals</vh></v>
-<v t="aum.20040802154241"><vh>Exceptions</vh></v>
-<v t="aum.20040811111244" a="E"><vh>Mixins</vh>
-<v t="aum.20040811111244.1"><vh>class KBase</vh></v>
-</v>
-<v t="aum.20040807013038" a="E"><vh>Main Engine</vh>
-<v t="aum.20040804122542" a="E"><vh>class KCore</vh>
-<v t="aum.20040813204308"><vh>attributes</vh></v>
-<v t="aum.20040804122542.1"><vh>__init__</vh></v>
-<v t="aum.20040804123117"><vh>subscribe</vh></v>
-<v t="aum.20040804123350"><vh>unsubscribe</vh></v>
-<v t="aum.20040804123526"><vh>threadRxPackets</vh></v>
-<v t="aum.20040805123632"><vh>threadHousekeeping</vh></v>
-<v t="aum.20040804132551"><vh>nodeWhichOwnsSock</vh></v>
-<v t="aum.20040812152603"><vh>cycle</vh></v>
-<v t="aum.20040807052750"><vh>run</vh></v>
-<v t="aum.20040807053418"><vh>stop</vh></v>
-<v t="aum.20040807114327"><vh>runClient</vh></v>
-<v t="aum.20040813204858"><vh>select</vh></v>
-</v>
-<v t="aum.20040804130347"><vh>create instance</vh></v>
-</v>
-<v t="aum.20040807013038.1" a="E"><vh>Basic Classes</vh>
-<v t="aum.20040807013038.2" a="E"><vh>Node-local Storage</vh>
-<v t="aum.20040803005037" a="E"><vh>class KStorageBase</vh>
-<v t="aum.20040803005037.1"><vh>__init__</vh></v>
-<v t="aum.20040803005037.2"><vh>putRefs</vh></v>
-<v t="aum.20040803005037.3"><vh>getRefs</vh></v>
-<v t="aum.20040803005037.4"><vh>putKey</vh></v>
-<v t="aum.20040803005309"><vh>getKey</vh></v>
-<v t="aum.20040803134838" a="E"><vh>private methods</vh>
-<v t="aum.20040803134838.1"><vh>_expandRefsList</vh></v>
-</v>
-</v>
-<v t="aum.20040803010321" a="E"><vh>class KStorageFile</vh>
-<v t="aum.20040803010321.1"><vh>__init__</vh></v>
-<v t="aum.20040803134418"><vh>putRefs</vh></v>
-<v t="aum.20040803140601"><vh>getRefs</vh></v>
-<v t="aum.20040808133629"><vh>putKey</vh></v>
-<v t="aum.20040808133941"><vh>getKey</vh></v>
-</v>
-</v>
-<v t="aum.20040802154241.1" a="E"><vh>class KHash</vh>
-<v t="aum.20040802154241.2"><vh>__init__</vh></v>
-<v t="aum.20040802164545"><vh>__str__</vh></v>
-<v t="aum.20040808160207"><vh>asHex</vh></v>
-<v t="aum.20040804001454"><vh>distance</vh></v>
-<v t="aum.20040813015858"><vh>rawdistance</vh></v>
-<v t="aum.20040802165054"><vh>operators</vh></v>
-</v>
-<v t="aum.20040803013434"><vh>class KBucket</vh>
-<v t="aum.20040803013434.1"><vh>__init__</vh></v>
-<v t="aum.20040803013638.1"><vh>justSeenPeer</vh></v>
-<v t="aum.20040805182736"><vh>__iter__</vh></v>
-</v>
-<v t="aum.20040802155814.2" a="E"><vh>class KPeer</vh>
-<v t="aum.20040802155814.3"><vh>__init__</vh></v>
-<v t="aum.20040802160335"><vh>send_ping</vh></v>
-<v t="aum.20040802160828"><vh>send_store</vh></v>
-<v t="aum.20040802160828.1"><vh>send_findNode</vh></v>
-<v t="aum.20040802160828.2"><vh>send_findData</vh></v>
-<v t="aum.20040805040409"><vh>send_reply</vh></v>
-<v t="aum.20040802184641"><vh>send_raw</vh></v>
-<v t="aum.20040804014643"><vh>justSeen</vh></v>
-<v t="aum.20040803143007" a="E"><vh>lowlevel</vh>
-<v t="aum.20040803143007.1"><vh>__str__</vh></v>
-<v t="aum.20040803143007.2"><vh>__repr__</vh></v>
-<v t="aum.20040804213616"><vh>__eq__</vh></v>
-<v t="aum.20040804220932"><vh>__ne__</vh></v>
-</v>
-</v>
-</v>
-<v t="aum.20040807013038.3" a="E"><vh>RPC Classes</vh>
-<v t="aum.20040805012557" a="E"><vh>class KRpc</vh>
-<v t="aum.20040805154232"><vh>attribs</vh></v>
-<v t="aum.20040805012557.1"><vh>__init__</vh></v>
-<v t="aum.20040806144708"><vh>__del__</vh></v>
-<v t="aum.20040806144812"><vh>__str__</vh></v>
-<v t="aum.20040806144829"><vh>__repr__</vh></v>
-<v t="aum.20040805001742"><vh>bindPeerReply</vh></v>
-<v t="aum.20040805001926"><vh>unbindPeerReply</vh></v>
-<v t="aum.20040805004949"><vh>unbindAll</vh></v>
-<v t="aum.20040805013630"><vh>start</vh></v>
-<v t="aum.20040805001258"><vh>execute</vh></v>
-<v t="aum.20040805030707"><vh>terminate</vh></v>
-<v t="aum.20040805185902"><vh>returnValue</vh></v>
-<v t="aum.20040805013903"><vh>on_reply</vh></v>
-<v t="aum.20040805013957"><vh>on_tick</vh></v>
-</v>
-<v t="aum.20040807013038.4" a="E"><vh>PING</vh>
-<v t="aum.20040805014353" a="E"><vh>class KRpcPing</vh>
-<v t="aum.20040805154253"><vh>attribs</vh></v>
-<v t="aum.20040805014353.1"><vh>__init__</vh></v>
-<v t="aum.20040805014900"><vh>start</vh></v>
-<v t="aum.20040805014900.1"><vh>on_reply</vh></v>
-<v t="aum.20040805032351"><vh>on_tick</vh></v>
-</v>
-</v>
-<v t="aum.20040807013038.5" a="E"><vh>FIND_NODE</vh>
-<v t="aum.20040806223556" a="E"><vh>class KPeerQueryRecord</vh>
-<v t="aum.20040807033258"><vh>__init__</vh></v>
-<v t="aum.20040807033258.1"><vh>hasTimedOut</vh></v>
-<v t="aum.20040807033258.2"><vh>__cmp__</vh></v>
-<v t="aum.20040807033818"><vh>__lt__ etc</vh></v>
-<v t="aum.20040807033818.1"><vh>isCloserThanAllOf</vh></v>
-<v t="aum.20040807034729"><vh>isCloserThanOneOf</vh></v>
-</v>
-<v t="aum.20040806223857" a="E"><vh>class KPeerQueryTable</vh>
-<v t="aum.20040807004327"><vh>__init__</vh></v>
-<v t="aum.20040807004327.1"><vh>setlist</vh></v>
-<v t="aum.20040807004327.5"><vh>getExpired</vh></v>
-<v t="aum.20040807004327.6"><vh>purgeExpired</vh></v>
-<v t="aum.20040807004327.11"><vh>sort</vh></v>
-<v t="aum.20040807004327.12"><vh>select</vh></v>
-<v t="aum.20040811163127"><vh>count</vh></v>
-<v t="aum.20040811221318"><vh>changeState</vh></v>
-<v t="aum.20040807004327.13"><vh>filter</vh></v>
-<v t="aum.20040807004327.14"><vh>purge</vh></v>
-<v t="aum.20040807004327.15"><vh>chooseN</vh></v>
-<v t="aum.20040807004434"><vh>__str__</vh></v>
-<v t="aum.20040807013835"><vh>newtable</vh></v>
-<v t="aum.20040811235333"><vh>dump</vh></v>
-<v t="aum.20040807013944" a="E"><vh>list-like methods</vh>
-<v t="aum.20040807004327.2"><vh>extend</vh></v>
-<v t="aum.20040807004327.3"><vh>append</vh></v>
-<v t="aum.20040807004327.4"><vh>remove</vh></v>
-<v t="aum.20040807004327.7"><vh>__getitem__</vh></v>
-<v t="aum.20040807004327.8"><vh>__len__</vh></v>
-<v t="aum.20040807004327.9"><vh>__getslice__</vh></v>
-<v t="aum.20040807004327.10"><vh>__iter__</vh></v>
-<v t="aum.20040807014538"><vh>__add__</vh></v>
-<v t="aum.20040807044007"><vh>__contains__</vh></v>
-</v>
-</v>
-<v t="aum.20040805130159" a="E"><vh>class KRpcFindNode</vh>
-<v t="aum.20040805143215"><vh>spec info comments</vh></v>
-<v t="aum.20040805154306"><vh>attribs</vh></v>
-<v t="aum.20040805130514"><vh>__init__</vh></v>
-<v t="aum.20040805140416" a="E"><vh>start</vh></v>
-<v t="aum.20040806234241"><vh>sendSomeQueries</vh></v>
-<v t="aum.20040807044007.1"><vh>sendOneQuery</vh></v>
-<v t="aum.20040805185215"><vh>findClosestPeersInitial</vh></v>
-<v t="aum.20040806002319"><vh>addPeerIfCloser</vh></v>
-<v t="aum.20040806132808"><vh>isCloserThanQueried</vh></v>
-<v t="aum.20040805140632"><vh>on_reply</vh></v>
-<v t="aum.20040805141509"><vh>on_tick</vh></v>
-<v t="aum.20040811221628"><vh>checkEndOfRound</vh></v>
-<v t="aum.20040811222934"><vh>gotAnyCloser</vh></v>
-<v t="aum.20040807003220"><vh>returnTheBestWeGot</vh></v>
-<v t="aum.20040807013411"><vh>returnValue</vh></v>
-<v t="aum.20040813200853"><vh>reportStats</vh></v>
-</v>
-</v>
-<v t="aum.20040807013038.6" a="E"><vh>FIND_DATA</vh>
-<v t="aum.20040805153146" a="E"><vh>class KRpcFindData</vh>
-<v t="aum.20040805154321"><vh>attribs</vh></v>
-<v t="aum.20040808153427"><vh>start</vh></v>
-<v t="aum.20040808163651"><vh>on_reply</vh></v>
-<v t="aum.20040816141128"><vh>on_gotValue</vh></v>
-<v t="aum.20040816141128.1"><vh>on_gotChunk</vh></v>
-<v t="aum.20040816014757"><vh>returnValue</vh></v>
-</v>
-</v>
-<v t="aum.20040807013038.7" a="E"><vh>STORE</vh>
-<v t="aum.20040805153315" a="E"><vh>class KRpcStore</vh>
-<v t="aum.20040805154344"><vh>attribs</vh></v>
-<v t="aum.20040808134739"><vh>__init__</vh></v>
-<v t="aum.20040808134739.1"><vh>start</vh></v>
-<v t="aum.20040816133040"><vh>storeSplit</vh></v>
-<v t="aum.20040816135222"><vh>on_doneChunkManifest</vh></v>
-<v t="aum.20040816135222.1"><vh>on_doneChunk</vh></v>
-<v t="aum.20040808140937"><vh>returnValue</vh></v>
-<v t="aum.20040808135302"><vh>on_doneFindNode</vh></v>
-<v t="aum.20040808140937.1"><vh>on_reply</vh></v>
-<v t="aum.20040808140937.2"><vh>on_tick</vh></v>
-</v>
-</v>
-<v t="aum.20040815164410" a="E"><vh>PINGALL</vh>
-<v t="aum.20040815164410.1" a="E"><vh>class KRpcPingAll</vh>
-<v t="aum.20040815164410.2"><vh>attribs</vh></v>
-<v t="aum.20040815164410.3"><vh>__init__</vh></v>
-<v t="aum.20040815164410.4"><vh>start</vh></v>
-<v t="aum.20040815164410.7"><vh>on_reply</vh></v>
-<v t="aum.20040815164410.8"><vh>on_tick</vh></v>
-<v t="aum.20040815164410.5"><vh>returnValue</vh></v>
-</v>
-</v>
-</v>
-<v t="aum.20040813232532" a="E"><vh>Node Socket Server</vh>
-<v t="aum.20040813232532.1" a="E"><vh>class KNodeServer</vh>
-<v t="aum.20040813233036"><vh>__init__</vh></v>
-<v t="aum.20040813234214"><vh>serve_forever</vh></v>
-</v>
-<v t="aum.20040813232532.2" a="E"><vh>class KNodeReqHandler</vh>
-<v t="aum.20040813232532.3"><vh>handle</vh></v>
-<v t="aum.20040814004432"><vh>finish</vh></v>
-</v>
-<v t="aum.20040814001156" a="E"><vh>class KNodeClient</vh>
-<v t="aum.20040814001156.1"><vh>__init__</vh></v>
-<v t="aum.20040814001912"><vh>hello</vh></v>
-<v t="aum.20040814001456"><vh>connect</vh></v>
-<v t="aum.20040814001522"><vh>close</vh></v>
-<v t="aum.20040814001456.2"><vh>get</vh></v>
-<v t="aum.20040814002236"><vh>put</vh></v>
-<v t="aum.20040814103533"><vh>addref</vh></v>
-<v t="aum.20040814131117"><vh>getref</vh></v>
-<v t="aum.20040815170456"><vh>pingall</vh></v>
-<v t="aum.20040814003559"><vh>kill</vh></v>
-<v t="aum.20040814002411"><vh>__getitem__</vh></v>
-<v t="aum.20040814002411.1"><vh>__setitem__</vh></v>
-</v>
-</v>
-<v t="aum.20040807013038.8" a="E"><vh>NODE</vh>
-<v t="aum.20040802161601" a="E"><vh>class KNode</vh>
-<v t="aum.20040813204308.1"><vh>attributes</vh></v>
-<v t="aum.20040802184641.1"><vh>__init__</vh></v>
-<v t="aum.20040808203152"><vh>__del__</vh></v>
-<v t="aum.20040805001449" a="E"><vh>application-level</vh>
-<v t="aum.20040802161601.1"><vh>start</vh></v>
-<v t="aum.20040802161601.2"><vh>stop</vh></v>
-<v t="aum.20040802161601.3"><vh>get</vh></v>
-<v t="aum.20040802161601.4"><vh>put</vh></v>
-<v t="aum.20040803015148"><vh>addref</vh></v>
-<v t="aum.20040802161601.5"><vh>__getitem__</vh></v>
-<v t="aum.20040802161601.6"><vh>__setitem__</vh></v>
-</v>
-<v t="aum.20040803131111.2" a="E"><vh>peer/rpc methods</vh>
-<v t="aum.20040803201812.1"><vh>_ping</vh></v>
-<v t="aum.20040815170327"><vh>_pingall</vh></v>
-<v t="aum.20040805140236"><vh>_findnode</vh></v>
-<v t="aum.20040805153555"><vh>_finddata</vh></v>
-<v t="aum.20040805153555.1"><vh>_store</vh></v>
-<v t="aum.20040803134102"><vh>_findPeer</vh></v>
-</v>
-<v t="aum.20040803131210" a="E"><vh>comms methods</vh>
-<v t="aum.20040803131210.1"><vh>_sendRaw</vh></v>
-</v>
-<v t="aum.20040803131210.2" a="E"><vh>engine</vh>
-<v t="aum.20040803131210.3"><vh>_threadRx</vh></v>
-<v t="aum.20040804205057"><vh>_doChug</vh></v>
-<v t="aum.20040804132053"><vh>_doRx</vh></v>
-<v t="aum.20040804200916"><vh>_doHousekeeping</vh></v>
-</v>
-<v t="aum.20040803144052" a="E"><vh>event handling</vh>
-<v t="aum.20040803163704"><vh>_on_ping</vh></v>
-<v t="aum.20040804013647"><vh>_on_findNode</vh></v>
-<v t="aum.20040804014115.1"><vh>_on_findData</vh></v>
-<v t="aum.20040808142950"><vh>_on_store</vh></v>
-<v t="aum.20040805040237"><vh>_on_reply</vh></v>
-<v t="aum.20040803163704.1"><vh>_on_unknown</vh></v>
-</v>
-<v t="aum.20040813234004" a="E"><vh>Socket Client Server</vh>
-<v t="aum.20040813234004.1"><vh>serve</vh></v>
-</v>
-<v t="aum.20040803142127" a="E"><vh>lowlevel stuff</vh>
-<v t="aum.20040803142127.1"><vh>__str__</vh></v>
-<v t="aum.20040803142127.2"><vh>__repr__</vh></v>
-<v t="aum.20040803193605"><vh>_msgIdAlloc</vh></v>
-<v t="aum.20040803200819"><vh>_normalisePeer</vh></v>
-<v t="aum.20040804130722"><vh>__del__</vh></v>
-</v>
-</v>
-</v>
-<v t="aum.20040803132156" a="E"><vh>funcs</vh>
-<v t="aum.20040814110540"><vh>userI2PDir</vh></v>
-<v t="aum.20040814110755"><vh>nodePidfile</vh></v>
-<v t="aum.20040805031135"><vh>messageEncode</vh></v>
-<v t="aum.20040805031135.1"><vh>messageDecode</vh></v>
-<v t="aum.20040808133629.1"><vh>shahash</vh></v>
-<v t="aum.20040803132528.1"><vh>log</vh></v>
-<v t="aum.20040810224601"><vh>logexc</vh></v>
-<v t="aum.20040814112703"><vh>spawnproc</vh></v>
-<v t="aum.20040814112703.1"><vh>killproc</vh></v>
-<v t="aum.20040814120624"><vh>i2psocket</vh></v>
-<v t="aum.20040813212609"><vh>usage</vh></v>
-<v t="aum.20040814015747"><vh>err</vh></v>
-<v t="aum.20040813211551" a="TV"><vh>main</vh></v>
-</v>
-<v t="aum.20040803141131" a="E"><vh>MAINLINE</vh>
-<v t="aum.20040812110124"><vh>mainline</vh></v>
-</v>
-</v>
-<v t="aum.20040815000938" a="E"><vh>Deployment</vh>
-<v t="aum.20040814132559" a="E" tnodeList="aum.20040814132559,aum.20040814140643"><vh>@file-nosent stasher</vh>
-<v t="aum.20040814140643"><vh>contents</vh></v>
-</v>
-<v t="aum.20040814134235" a="E" tnodeList="aum.20040814134235,aum.20040814140643"><vh>@file-nosent stasher-launch.py</vh>
-<v t="aum.20040814140643"><vh>contents</vh></v>
-</v>
-<v t="aum.20040814133042" tnodeList="aum.20040814133042"><vh>@file release.sh</vh></v>
-<v t="aum.20040814234015" tnodeList="aum.20040814234015"><vh>@file setup-stasher.py</vh></v>
-<v t="aum.20040815001048" a="E" tnodeList="aum.20040815001048,aum.20040815143611,aum.20040815143611.1,aum.20040815143611.2,aum.20040815143611.3"><vh>@file-nosent README.txt</vh>
-<v t="aum.20040815143611"><vh>installing-from-cvs</vh></v>
-<v t="aum.20040815143611.1"><vh>doze warning</vh></v>
-<v t="aum.20040815143611.2"><vh>alpha warning</vh></v>
-<v t="aum.20040815143611.3"><vh>using</vh></v>
-</v>
-<v t="aum.20040815143009" a="E" tnodeList="aum.20040815143009,aum.20040815143611.4,aum.20040815143611.2,aum.20040815143611.3"><vh>@file-nosent README-tarball.txt</vh>
-<v t="aum.20040815143611.4"><vh>installing as tarball</vh></v>
-<v t="aum.20040815143611.2"><vh>alpha warning</vh></v>
-<v t="aum.20040815143611.3"><vh>using</vh></v>
-</v>
-</v>
-<v t="aum.20040815000938.1" a="E"><vh>Testing</vh>
-<v t="aum.20040813202242" tnodeList="aum.20040813202242,aum.20040813203116,aum.20040813203621,aum.20040807013038.9,aum.20040811013733,aum.20040812013918,aum.20040813203339,aum.20040813203339.1,aum.20040804140615,aum.20040804142640,aum.20040804140958,aum.20040812125235,aum.20040804141700,aum.20040804141700.1,aum.20040812015208,aum.20040804141801,aum.20040804141824,aum.20040809222157,aum.20040810142448,aum.20040812221935,aum.20040812230933,aum.20040812020746,aum.20040810122748,aum.20040810125759,aum.20040810132855,aum.20040810131309,aum.20040810142611,aum.20040813013718,aum.20040810115020,aum.20040810141626,aum.20040806232701,aum.20040806232714,aum.20040813202517,aum.20040803141131.1,aum.20040813211933,aum.20040811160223,aum.20040811223457,aum.20040813202541,aum.20040813202541.1"><vh>@file ktest.py</vh>
-<v t="aum.20040813203116"><vh>imports</vh></v>
-<v t="aum.20040813203621"><vh>constants</vh></v>
-<v t="aum.20040807013038.9" a="E"><vh>TEST NETWORK</vh>
-<v t="aum.20040811013733"><vh>class KTestSocket</vh></v>
-<v t="aum.20040812013918"><vh>class KTestMap</vh></v>
-<v t="aum.20040813203339"><vh>class KCore</vh></v>
-<v t="aum.20040813203339.1"><vh>class KNode</vh></v>
-<v t="aum.20040804140615" a="E"><vh>class KTestNetwork</vh>
-<v t="aum.20040804142640"><vh>attribs</vh></v>
-<v t="aum.20040804140958"><vh>__init__</vh></v>
-<v t="aum.20040812125235"><vh>__del__</vh></v>
-<v t="aum.20040804141700"><vh>__getitem__</vh></v>
-<v t="aum.20040804141700.1"><vh>__len__</vh></v>
-<v t="aum.20040812015208"><vh>connect</vh></v>
-<v t="aum.20040804141801"><vh>start</vh></v>
-<v t="aum.20040804141824"><vh>stop</vh></v>
-<v t="aum.20040809222157"><vh>dump</vh></v>
-<v t="aum.20040810142448"><vh>dumplong</vh></v>
-<v t="aum.20040812221935"><vh>findpath</vh></v>
-<v t="aum.20040812230933"><vh>testconnectivity</vh></v>
-<v t="aum.20040812020746"><vh>purge</vh></v>
-<v t="aum.20040810122748"><vh>whohas</vh></v>
-<v t="aum.20040810125759"><vh>whocanfind</vh></v>
-<v t="aum.20040810132855"><vh>findnode</vh></v>
-<v t="aum.20040810131309"><vh>closestto</vh></v>
-<v t="aum.20040810142611"><vh>getPeer</vh></v>
-<v t="aum.20040813013718"><vh>getPeerIdx</vh></v>
-<v t="aum.20040810115020"><vh>getPeerName</vh></v>
-<v t="aum.20040810141626"><vh>dumpids</vh></v>
-<v t="aum.20040806232701"><vh>__str__</vh></v>
-<v t="aum.20040806232714"><vh>__repr__</vh></v>
-</v>
-</v>
-<v t="aum.20040813202517" a="E"><vh>Funcs</vh>
-<v t="aum.20040803141131.1"><vh>test</vh></v>
-<v t="aum.20040813211933"><vh>test1</vh></v>
-<v t="aum.20040811160223"><vh>debug</vh></v>
-<v t="aum.20040811223457"><vh>doput</vh></v>
-</v>
-<v t="aum.20040813202541" a="E"><vh>MAINLINE</vh>
-<v t="aum.20040813202541.1"><vh>mainline</vh></v>
-</v>
-</v>
-<v t="aum.20040814191506" tnodeList="aum.20040814191506"><vh>@file node1.sh</vh></v>
-<v t="aum.20040814191718" tnodeList="aum.20040814191718"><vh>@file node2.sh</vh></v>
-</v>
-<v t="aum.20040815000938.2" a="E"><vh>Utility</vh>
-<v t="aum.20040805155412"><vh>@file pybloom.py</vh>
-<v t="aum.20040805155412.1"><vh>imports</vh></v>
-<v t="aum.20040805155412.2"><vh>globals</vh></v>
-<v t="aum.20040805155412.3"><vh>mixarray_init</vh></v>
-<v t="aum.20040805155412.4" a="E"><vh>class Bloom</vh>
-<v t="aum.20040805160344"><vh>attribs</vh></v>
-<v t="aum.20040805155412.5"><vh>__init__</vh></v>
-<v t="aum.20040805155412.6"><vh>_make_array</vh></v>
-<v t="aum.20040805155412.7"><vh>_hashfunc</vh></v>
-<v t="aum.20040805155412.8"><vh>insert</vh></v>
-<v t="aum.20040805155412.9"><vh>__contains__</vh></v>
-</v>
-<v t="aum.20040805155412.10" a="E"><vh>class CountedBloom</vh>
-<v t="aum.20040805155412.11"><vh>__init__</vh></v>
-<v t="aum.20040805155412.12"><vh>insert</vh></v>
-<v t="aum.20040805155412.13"><vh>__contains__</vh></v>
-<v t="aum.20040805155412.14"><vh>__delitem__</vh></v>
-</v>
-<v t="aum.20040805155412.15"><vh>mainline</vh></v>
-</v>
-<v t="aum.20040808213724" tnodeList="aum.20040808213724,aum.20040808221610,aum.20040808221925,aum.20040809135303,aum.20040809162443,aum.20040809135303.1,aum.20040809135303.2,aum.20040809135303.3,aum.20040809145905,aum.20040809150949,aum.20040809150949.1,aum.20040808221610.1,aum.20040808223205,aum.20040808221610.3,aum.20040808221610.4,aum.20040809160231,aum.20040808231518,aum.20040808224404,aum.20040809135512,aum.20040809145111,aum.20040809135512.1"><vh>@file-nosent hashcash.py</vh>
-<v t="aum.20040808221610"><vh>imports</vh></v>
-<v t="aum.20040808221925"><vh>globals</vh></v>
-<v t="aum.20040809135303" a="E"><vh>class HashCash</vh>
-<v t="aum.20040809162443"><vh>attributes</vh></v>
-<v t="aum.20040809135303.1"><vh>__init__</vh></v>
-<v t="aum.20040809135303.2"><vh>generate</vh></v>
-<v t="aum.20040809135303.3"><vh>verify</vh></v>
-<v t="aum.20040809145905"><vh>_checkBase64</vh></v>
-<v t="aum.20040809150949"><vh>_enc64</vh></v>
-<v t="aum.20040809150949.1"><vh>_dec64</vh></v>
-</v>
-<v t="aum.20040808221610.1"><vh>generate</vh></v>
-<v t="aum.20040808223205"><vh>verify</vh></v>
-<v t="aum.20040808221610.3"><vh>binify</vh></v>
-<v t="aum.20040808221610.4"><vh>intify</vh></v>
-<v t="aum.20040809160231"><vh>_randomString</vh></v>
-<v t="aum.20040808231518"><vh>psyco</vh></v>
-<v t="aum.20040808224404"><vh>test</vh></v>
-<v t="aum.20040809135512"><vh>ctest</vh></v>
-<v t="aum.20040809145111"><vh>ntest</vh></v>
-<v t="aum.20040809135512.1"><vh>mainline</vh></v>
-</v>
-</v>
-<v t="aum.20040804223241"><vh>JUNK</vh>
-<v t="aum.20040811162256" a="E"><vh>Findnode RPC on_reply</vh>
-<v t="aum.20040811162302"><vh>on_reply</vh></v>
-</v>
-<v t="aum.20040804204524"><vh>class KPendingResultBase</vh>
-<v t="aum.20040804204524.1"><vh>__init__</vh></v>
-<v t="aum.20040804210421"><vh>append</vh></v>
-<v t="aum.20040804210911"><vh>wait</vh></v>
-<v t="aum.20040804211058"><vh>check</vh></v>
-<v t="aum.20040804212714"><vh>destroySelf</vh></v>
-<v t="aum.20040804211248"><vh>on_tick</vh></v>
-<v t="aum.20040804211539"><vh>on_packet</vh></v>
-<v t="aum.20040804205740"><vh>__cmp__</vh></v>
-</v>
-<v t="aum.20040804212126"><vh>class KPendingResultPing</vh>
-<v t="aum.20040804212126.1"><vh>__init__</vh></v>
-<v t="aum.20040804212342"><vh>on_tick</vh></v>
-<v t="aum.20040804212607"><vh>on_packet</vh></v>
-</v>
-</v>
-</v>
-</vnodes>
-<tnodes>
-<t tx="aum.20040802153926"></t>
-<t tx="aum.20040802154007">@first #! /usr/bin/env python
-"""
-A simple implementation of the
-U{Kademlia&lt;http://www.infoanarchy.org/wiki/wiki.pl?Kademlia&gt;}
-P2P distributed storage and retrieval protocol, designed to
-utilise the U{I2P&lt;http://www.i2p.net&gt;} stealth network as its transport.
-
-Most application developers will only need to know about the L{KNode} class
-"""
-
-# I strongly recommend that when editing this file, you use the Leo
-# outlining and literate programming editor - http://leo.sf.net
-# If Leo doesn't agree with your religion, please try to leave the markups intact
-
-@others
-
-</t>
-<t tx="aum.20040802154241"># define our exceptions
-
-class KValueTooLarge(Exception):
-    """
-    Trying to insert a value of excessive size into the network.
-    Maximum key size is L{maxValueSize}
-    """
-
-class KBadHash(Exception):
-    """
-    Invalid hash string
-    """
-
-class KNotImplemented(Exception):
-    """
-    A required method was not implemented
-    """
-
-class KBadNode(Exception):
-    """
-    Invalid Node object
-    """
-
-class KBadPeer(Exception):
-    """
-    Invalid Peer object - should be a KPeer
-    """
-
-class KBadDest(Exception):
-    """Invalid I2P Node Dest"""
-
-</t>
-<t tx="aum.20040802154241.1">class KHash(KBase):
-    """
-    Wraps 160-bit hashes as abstract objects, on which
-    operations such as xor, &lt;, &gt;, etc can be performed.
-    
-    Kademlia node ids and keys are held as objects
-    of this class.
-
-    Internally, hashes are stored as python long ints
-    """
-    @others
-</t>
-<t tx="aum.20040802154241.2">def __init__(self, val=None, **kw):
-    """
-    Create a new hash object.
-    
-    val can be one of the following:
-        - None (default) - a random value will be created
-        - long int - this will be used as the raw hash
-        - string - the string will be hashed and stored
-        - another KHash object - its value will be taken
-        - a KNode or KPeer object - its hash will be taken
-
-    If val is not given, a raw hash value can be passed in
-    with the keyword 'raw'. Such value must be a python long int
-    or a 20-char string
-    """
-    self.value = 0L
-    if val:
-        if isinstance(val, KHash):
-            self.value = val.value
-        elif type(val) in [type(0), type(0L)]:
-            self.value = long(val)
-        elif isinstance(val, KNode) or isinstance(val, KPeer):
-            self.value = val.id.value
-        else:
-            raw = self.raw = shahash(val, bin=1)
-            for c in raw:
-                self.value = self.value * 256 + ord(c)
-    else:
-        rawval = kw.get('raw', None)
-        if rawval == None:
-            # generate random
-            random.seed()
-            for i in range(20):
-                self.value = self.value * 256 + random.randint(0, 256)
-        elif type(rawval) in [type(0), type(0L)]:
-            self.value = long(rawval)
-        elif type(rawval) == type(""):
-            if len(rawval) == 20:
-                for i in rawval:
-                    self.value = self.value * 256 + ord(i)
-            elif len(rawval) == 40:
-                try:
-                    self.value = long(rawval, 16)
-                except:
-                    raise KBadHash(rawval)
-            else:
-                raise KBadHash(rawval)
-        else:
-            print "rawval=%s %s %s" % (type(rawval), rawval.__class__, repr(rawval))
-            raise KBadHash(rawval)
-
-</t>
-<t tx="aum.20040802154945">import sys, os, types, sha, random, threading, thread, traceback, Queue
-import time, math, random, pickle, getopt, re
-import signal
-
-# some windows-specifics (yggghh)
-if sys.platform == 'win32':
-    try:
-        import win32api
-        import win32process
-        import _winreg
-    except:
-        print "Python win32 extensions not installed."
-        print "Please go to http://sourceforge.net/project/showfiles.php?group_id=78018"
-        print "and download/install the file pywin32-202.win32-py%s.%s.exe" % \
-            sys.version_info[:2]
-        sys.exit(1)
-
-from StringIO import StringIO
-from pdb import set_trace
-
-try:
-    import bencode
-except:
-    print "The bencode module is missing from your python installation."
-    print "Are you sure you installed Stasher correctly?"
-    sys.exit(1)
-
-try:
-    import i2p.socket
-    import i2p.select
-    import i2p.pylib
-    SocketServer = i2p.pylib.SocketServer
-    socket = i2p.pylib.socket
-except:
-    print "You don't appear to have the I2P Python modules installed."
-    print "Not good. Stasher totally needs them."
-    print "Please to to i2p/apps/sam/python in your I2P cvs tree, and"
-    print "install the core I2P python modules first"
-    sys.exit(1)
-
-</t>
-<t tx="aum.20040802155814.2">class KPeer(KBase):
-    """
-    Encapsulates a peer node of a L{KNode},
-    storing its ID and contact info
-    """
-    @others
-</t>
-<t tx="aum.20040802155814.3">def __init__(self, node, dest):
-    """
-    Create a ref to a kademlia peer node
-    
-    Arguments:
-        - node - reference to node which has the relationship
-          to this peer
-        - dest - the peer's I2P destination, as base64
-    """
-    if not isinstance(node, KNode):
-        raise KBadNode(node)
-    if not isinstance(dest, str):
-        raise KBadDest(dest)
-
-    self.node = node
-    self.dest = dest
-    self.id = KHash(dest)
-
-    self.justSeen()
-
-</t>
-<t tx="aum.20040802160335">def send_ping(self, **kw):
-    """
-    Sends a ping to remote peer
-    """
-    self.send_raw(type="ping", **kw)
-</t>
-<t tx="aum.20040802160828">def send_store(self, **kw):
-    """
-    sends a store command to peer
-    """
-    self.log(4, "\npeer %s\ndest %s...\nsending store cmd: %s" % (self, self.dest[:12], repr(kw)))
-
-    self.send_raw(type="store", **kw)
-
-</t>
-<t tx="aum.20040802160828.1">def send_findNode(self, hash, **kw):
-    """
-    sends a findNode command to peer
-    """
-    if not isinstance(hash, KHash):
-        raise KBadHash
-
-    self.log(5, "\nquerying peer %s\ntarget hash %s" % (self, hash))
-
-    self.send_raw(type="findNode", hash=hash.value, **kw)
-
-</t>
-<t tx="aum.20040802160828.2">def send_findData(self, hash, **kw):
-    """
-    sends a findData command to peer
-    """
-    if not isinstance(hash, KHash):
-        raise KBadHash
-
-    self.log(5, "\nquerying peer %s\ntarget hash %s" % (self, hash))
-
-    self.send_raw(type="findData", hash=hash.value, **kw)
-
-</t>
-<t tx="aum.20040802161601">class KNode(KBase):
-    """
-    B{Public API to this Kademlia implementation}
-    
-    You should not normally need to use, or even be aware of, 
-    any of the other classes
-
-    And in this class, the only methods you need to worry about are:
-        - L{start} - starts the node running
-        - L{stop} - stops the node
-        - L{get} - retrieve a key value
-        - L{put} - stores a key value
-        - L{addref} - imports a noderef
-
-    This class implements a single kademlia node.
-    Within a single process, you can create as many nodes as you like.
-    """
-    @others
-</t>
-<t tx="aum.20040802161601.1">def start(self, doPings=True):
-    """
-    Starts the node running
-    """
-    # barf if already running
-    if self.isRunning:
-        self.log(3, "node %s is already running!" % self.name)
-        return
-
-    self.log(3, "starting node %s" % self.name)
-
-    # first step - ping all our peers
-    if doPings:
-        for peer in self.peers:
-            self.log(3, "doing initial ping\n%s\n%s" % (self, peer))
-            KRpcPing(self, peer=peer)
-
-    # first step - do a findNode against our own node id, and ping our
-    # neighbours
-    if greetPeersOnStartup:
-        neighbours = KRpcFindNode(self, hash=self.id).execute()
-        self.log(3, "neighbours=%s" % repr([n[:10] for n in neighbours]))
-        for n in neighbours:
-            n = self._normalisePeer(n)
-            KRpcPing(self, peer=n)
-
-    # note now that we're running
-    self.isRunning = True
-
-    # and enlist with the core
-    if runCore:
-        core.subscribe(self)
-    else:
-        # central core disabled, run our own receiver thread instead
-        thread.start_new_thread(self._threadRx, ())
-</t>
-<t tx="aum.20040802161601.2">def stop(self):
-    """
-    Shuts down the node
-    """
-    self.isRunning = 0
-    if runCore:
-        try:
-            core.unsubscribe(self)
-        except:
-            pass
-</t>
-<t tx="aum.20040802161601.3">def get(self, item, callback=None, **kw):
-    """
-    Attempts to retrieve data from the network
-    
-    Arguments:
-        - item - the key we desire
-        - callback - optional - if given, the get will be performed
-          asynchronously, and callback will be invoked upon completion, with
-          the result as first argument
-    Keywords:
-        - local - optional - if True, limits this search to this local node
-          default is False
-    
-    Returns:
-        - if no callback - the item value if the item was found, or None if not
-        - if callback, None is returned
-    """
-    def processResult(r):
-        if isinstance(r, str):
-            return r
-        return None
-
-    if callback:
-        # create a func to process callback result
-        def onCallback(res):
-            callback(processResult(res))
-    
-        self._finddata(item, onCallback, **kw)
-    else:
-        return processResult(self._finddata(item, **kw))
-
-</t>
-<t tx="aum.20040802161601.4">def put(self, key, value, callback=None, **kw):
-    """
-    Inserts a named key into the network
-
-    Arguments:
-        - key - one of:
-            - None - a secure key will be generated and used
-            - a KHash object
-            - a raw string which will be hashed into a KHash object
-        - val - a string, the value associated with the key
-
-    Keywords:
-        - local - default False - if True, limits the insert to the
-          local node
-
-    If the value is larger than L{maxValueSize}, a L{KValueTooLarge}
-    exception will occur.
-    """
-    return self._store(key, value, callback, **kw)
-
-</t>
-<t tx="aum.20040802161601.5">def __getitem__(self, item):
-    """
-    Allows dict-like accesses on the node object
-    """
-    return self.get(item)
-</t>
-<t tx="aum.20040802161601.6">def __setitem__(self, item, val):
-    """
-    Allows dict-like key setting on the node object
-    """
-    self.put(item, val)
-
-</t>
-<t tx="aum.20040802164545">def __str__(self):
-    return "&lt;KHash: 0x%x&gt;" % self.value
-
-def __repr__(self):
-    return str(self)
-
-</t>
-<t tx="aum.20040802165054">def __eq__(self, other):
-    #log(2, "KHash: comparing %s to %s" % (self, other))
-    res = self.value == getattr(other, 'value', None)
-    #self.log(2, "KHash: res = %s" % repr(res))
-    return res
-
-def __ne__(self, other):
-    return not (self == other)
-
-def __lt__(self, other):
-    return self.value &lt; other.value
-
-def __gt__(self, other):
-    return self.value &gt; other.value
-    
-def __le__(self, other):
-    return self.value &lt;= other.value
-
-def __ge__(self, other):
-    return self.value &gt;= other.value
-
-def __ne__(self, other):
-    return self.value != other.value
-
-def __xor__(self, other):
-    return self.value ^ other.value
-
-</t>
-<t tx="aum.20040802184641">def send_raw(self, **kw):
-    """
-    Sends a raw datagram to peer
-    
-    No arguments - just keywords, all of which must be strings or
-    other objects which can be bencoded
-    """
-    self.node._sendRaw(self, **kw)
-</t>
-<t tx="aum.20040802184641.1">def __init__(self, name, **kw):
-    """
-    Creates a kademlia node of name 'name'.
-
-    Name is mandatory, because each name is permanently written
-    to the SAM bridge's store
-    
-    I thought of supporting random name generation, but went off this
-    idea because names get permanently stored to SAM bridge's file
-
-    Arguments:
-        - name - mandatory - a short text name for the node, should
-          be alphanumerics, '-', '.', '_'
-          This name is used for the SAM socket session.
-    
-    Keywords:
-        - storage - optional - an instance of L{KStorageBase} or one of
-          its subclasses. If not given, default action is to instantiate
-          a L{KStorageFile} object against the given node name
-    """
-    # remember who we are
-    self.name = name
-
-    # not running yet, will launch when explicitly started, or implicitly
-    # when the first operation gets done
-    self.isRunning = False
-
-    # create socket and get its dest, and determine our node id
-    self.id = KHash("&lt;NONE&gt;")
-    self.log(5, "creating socket for node %s" % name)
-    self.log(5, "socket for node %s created" % name)
-    if self.SocketFactory == None:
-        self.SocketFactory = i2p.socket.socket
-    self.sock = self.SocketFactory(
-        "stashernode-"+name,
-        i2p.socket.SOCK_DGRAM,
-        samaddr=samAddr,
-        **kw)
-    #self.sockLock = threading.Lock() # prevents socket API reentrance
-    self.sock.setblocking(0)
-    self.dest = self.sock.dest
-    self.id = KHash(self.dest)
-
-    # create our buckets
-    self.buckets = []
-    for i in range(160):
-        self.buckets.append(KBucket())
-    
-    # create our storage object, default to new instance of KStorageFile
-    self.storage = kw.get('storage', KStorageFile(self))
-
-    # dig out all previously known nodes
-    self.peers = self.storage.getRefs()
-
-    # set up dict of callers awaiting replies
-    # keys are (peerobj, msgId) tuples, values are Queue.Queue objects
-    self.pendingPings = {}
-
-    # mapping of (peer, msgId) to RPC object, so when RPC replies come in,
-    # they can be passed directly to the RPC object concerned
-    self.rpcBindings = {}
-
-    # KRpc objects waiting for peer replies - used for checking for timeouts
-    self.rpcPending = []
-    
-    # miscellaneous shit
-    self._msgIdNext = 0
-    #self._msgIdLock = threading.Lock()
-
-    # register in global map
-    _nodes[name] = self
-
-
-</t>
-<t tx="aum.20040803005037">class KStorageBase(KBase):
-    """
-    Base class for node storage objects
-
-    This needs to be overridden by implementation-specific
-    solutions.
-    """
-    @others
-</t>
-<t tx="aum.20040803005037.1">def __init__(self, node, *args, **kw):
-    """
-    Override this method
-    
-    First argument should be a node instance
-    """
-    raise KNotImplemented
-
-</t>
-<t tx="aum.20040803005037.2">def putRefs(self, *refs):
-    """
-    Saves one or more noderefs
-    
-    Arguments:
-        - zero or more KPeer objects, or lists or tuples of objects
-    """
-    raise KNotImplemented
-</t>
-<t tx="aum.20040803005037.3">def getRefs(self):
-    """
-    Returns a list of KPeer objects, comprising refs
-    of peers known to this node
-    """
-    raise KNotImplemented
-
-</t>
-<t tx="aum.20040803005037.4">def putKey(self, key, value):
-    """
-    Stores value, a string, into the local storage
-    under key 'key'
-    """
-    raise KNotImplemented
-
-</t>
-<t tx="aum.20040803005309">def getKey(self, key):
-    """
-    Attempts to retrieve item from node's local, which was
-    stored with key 'key'.
-    
-    Returns value as a string if found, or None if not present
-    """
-    raise KNotImplemented
-</t>
-<t tx="aum.20040803010321">class KStorageFile(KStorageBase):
-    """
-    Implements node-local storage, using the local filesystem,
-    with the following hierarchy:
-        
-        - HOME ( ~ in linux, some other shit for windows)
-           - .i2pkademlia
-               - &lt;nodename&gt;
-                   - noderefs
-                       - &lt;node1 base64 hash&gt;
-                           - contains node dest, and other shit
-                       - ...
-                   - keys
-                       - &lt;keyname1&gt;
-                           - contains raw key value
-                       - ...
-
-    This is one ugly sukka, perhaps a db4, mysql etc implementation
-    would be better.
-    """
-    @others
-</t>
-<t tx="aum.20040803010321.1">def __init__(self, node, storeDir=None):
-    """
-    Creates a persistent storage object for node
-    'nodeName', based at directory 'storeDir' (default
-    is nodeDir
-    """
-    self.node = node
-    self.nodeName = node.name
-
-    if storeDir == None:
-        # work out local directory
-        self.topDir = userI2PDir()
-
-    # add node dir and subdirs
-    self.nodeDir = userI2PDir(self.nodeName)
-    
-    self.refsDir = os.path.join(self.nodeDir, "noderefs")
-    if not os.path.isdir(self.refsDir):
-        os.makedirs(self.refsDir)
-
-    self.keysDir = os.path.join(self.nodeDir, "keys")
-    if not os.path.isdir(self.keysDir):
-        os.makedirs(self.keysDir)
-
-</t>
-<t tx="aum.20040803013434">class KBucket(KBase):
-    """
-    Implements the 'k-bucket' object as required in Kademlia spec
-    """
-    @others
-</t>
-<t tx="aum.20040803013434.1">def __init__(self):
-    """
-    Creates a single k-bucket
-    """
-    # list of known nodes
-    # order is least recently seen at head, most recently seen at tail
-    self.nodes = []
-
-    # list of death-row records
-    # refer spec section 2.1, paragraph 2
-    # we deviate a little:
-    #   when we hear from a new peer, and the bucket is full,
-    #   we temporarily displace the old peer, and stick the new
-    #   peer at end of list, then send out a ping
-    #   If we hear from the old peer within a reasonable time,
-    #   the new peer gets evicted and replaced with the old peer
-    #
-    # this list holds 2-tuples (oldpeer, newpeer), where
-    # oldpeer is the least-recently-seen peer that we displaced, and
-    # newpeer is the new peer we just heard from.
-    self.deathrow = []
-
-</t>
-<t tx="aum.20040803013638">
-# --------------------------------------------
-# START USER-CONFIGURABLE CONSTANTS
-# --------------------------------------------
-
-# host:port to connect to I2P SAM Bridge
-samAddr = i2p.socket.samaddr
-
-# host:port to listen on for command line client
-clientAddr = "127.0.0.1:7659"
-
-defaultNodename = "0" # will be prefixed by 'stashernode'
-
-# maximum size of each stored item
-maxValueSize = 30000
-
-# maximum number of noderefs that can be stored in a bucket
-# (refer spec section 2.1, first paragraph)
-maxBucketSize = 20
-
-# number of peers to return from a search
-numSearchPeers = 3
-
-# maximum number of concurrent queries per findnode/finddata rpc
-maxConcurrentQueries = 10
-
-# number of peers to store onto
-numStorePeers = 10
-
-# Logger settings
-logFile = None
-logVerbosity = 2
-
-# data directory location - set to a path to override the default
-# which is the user's home dir
-dataDir = None
-
-# whether a node, on startup, should do a findnode on itself to
-# locate its closest neighbours
-greetPeersOnStartup = False
-#greetPeersOnStartup = True
-
-# multi-purpose testing flag
-testing = False
-#testing = True
-
-tunnelDepth = 0
-
-# set to True to enable single handler thread that manages all nodes,
-# or False to make each node run its own handler thread
-#runCore = False
-runCore = True
-
-# timeouts - calibrate as needed
-timeout = {
-    'ping' : 120,
-    'findNode' : 120,
-    'findData' : 120,
-    'store' : 120,
-    }
-
-logToSocket = None
-
-desperatelyDebugging = False
-
-if desperatelyDebugging:
-    runCoreInBackground = False
-else:
-    runCoreInBackground = True
-
-# --------------------------------------------
-# END OF USER-CONFIGURABLE CONSTANTS
-# --------------------------------------------
-
-# ----------------------------------------------
-# hack anything below this line at your own risk
-
-</t>
-<t tx="aum.20040803013638.1">def justSeenPeer(self, peer):
-    """
-    Tells the bucket that we've just seen a given node
-    """
-    nodes = self.nodes
-
-    if not isinstance(peer, KPeer):
-        raise KBadNode
-
-    try:
-        idx = nodes.index(peer)
-    except:
-        idx = -1    
-    if idx &gt;= 0:
-        del nodes[idx]
-        nodes.append(peer)
-    else:
-        nodes.append(peer)
-        
-    # might at some time need to implement death-row logic 
-    # when we set a bucket size limit - refer __init__
-</t>
-<t tx="aum.20040803015148">def addref(self, peer, doPing=False):
-    """
-    Given a peer node's destination, add it to our
-    buckets and internal data store
-    
-    Arguments:
-        - peer - one of:
-            - the I2P destination of the peer node, as
-              a base64 string
-            - a KNode object
-            - a KPeer object
-        - doPing - ping this node automatically (default False)
-    """
-    peer = self._normalisePeer(peer)
-
-    # remember peer if not already known
-    if peer.dest == self.dest:
-        self.log(3, "node %s, trying to add ref to ourself???" % self.name)
-        return peer
-    elif not self._findPeer(peer.dest):
-        self.peers.append(peer)
-        self.storage.putRefs(peer)
-    else:
-        self.log(4, "node %s, trying to add duplicate noderef %s" % (
-            self.name, peer))
-        return peer
-
-    # update our KBucket
-    dist = self.id.distance(peer.id)
-    self.buckets[dist].justSeenPeer(peer)
-
-    if doPing:
-        self.log(4, "doing initial ping\n%s\n%s" % (self, peer))
-        KRpcPing(self, peer=peer)
-
-    return peer
-
-</t>
-<t tx="aum.20040803131111.2"></t>
-<t tx="aum.20040803131210"></t>
-<t tx="aum.20040803131210.1">def _sendRaw(self, peer, **kw):
-    """
-    Serialises keywords passed, and sends this as a datagram
-    to node 'peer'
-    """
-    # update our KBucket
-    dist = self.id.distance(peer.id)
-    self.buckets[dist].justSeenPeer(peer)
-
-    # save ref to this peer
-    self.addref(peer)
-
-    params = dict(kw)
-    msgId = params.get('msgId', None)
-    if msgId == None:
-        msgId = params['msgId'] = self._msgIdAlloc()
-
-    objenc = messageEncode(params)
-    self.log(5, "node %s waiting for send lock" % self.name)
-    #self.sockLock.acquire()
-    self.log(5, "node %s got send lock" % self.name)
-    try:
-        self.sock.sendto(objenc, 0, peer.dest)
-    except:
-        traceback.print_exc()
-    #self.sockLock.release()
-    self.log(5, "node %s released send lock" % self.name)
-
-    self.log(4, "node %s sent %s to peer %s" % (self.name, params, peer.dest))
-    return msgId
-
-</t>
-<t tx="aum.20040803131210.2"></t>
-<t tx="aum.20040803131210.3">def _threadRx(self):
-    """
-    Thread which listens for incoming datagrams and actions
-    accordingly
-    """
-    self.log(3, "starting receiver thread for node %s" % self.name)
-
-    try:
-        # loop to drive the node
-        while self.isRunning:
-            self._doChug()
-    except:
-        traceback.print_exc()
-        self.log(3, "node %s - THREAD CRASH!" % self.name)
-
-    self.log(3, "receiver thread for node %s terminated" % self.name)
-
-</t>
-<t tx="aum.20040803132156">@others
-</t>
-<t tx="aum.20040803132528"># keep a dict of existing nodes, so we can prevent
-# client progs from creating 2 nodes of the same name
-_nodes = {}
-
-version = "0.0.1"
-
-</t>
-<t tx="aum.20040803132528.1">logLock = threading.Lock()
-
-def log(verbosity, msg, nPrev=0, clsname=None):
-
-    global logToSocket, logFile
-
-    # create logfile if not exists
-    if logFile == None:
-        logFile = os.path.join(userI2PDir(), "stasher.log")
-
-    # rip the stack
-    caller = traceback.extract_stack()[-(2+nPrev)]
-    path, line, func = caller[:3]
-    path = os.path.split(path)[1]
-
-    #print "func is type %s, val %s" % (type(func), repr(func))
-
-    #if hasattr(func, "im_class"):
-    #    func = 
-
-    if clsname:
-        func = clsname + "." + func
-
-    #msg = "%s:%s:%s():  %s" % (
-    #    path,
-    #    line,
-    #    func,
-    #    msg.replace("\n", "\n   + "))
-
-    msg = "%s():%s:  %s" % (
-        func,
-        line,
-        msg.replace("\n", "\n   + "))
-
-    # do better logging later
-    if verbosity &gt; logVerbosity:
-        return
-
-    if logToSocket:
-        try:
-            if isinstance(logToSocket, int):
-                portnum = logToSocket
-                logToSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-                connected = 0
-                while 1:
-                    try:
-                        logToSocket.connect(("localhost", portnum))
-                        break
-                    except socket.error:
-                        print "Please open an xterm/nc listening on %s" % logToSocket
-                        time.sleep(1)
-
-            logToSocket.send(msg+"\n")
-        except:
-            traceback.print_exc()
-    else:
-        print msg
-
-    logLock.acquire()
-    file(logFile, "a+").write(msg + "\n")
-    logLock.release()
-</t>
-<t tx="aum.20040803134102">def _findPeer(self, dest):
-    """
-    Look up our table of current peers for a given dest.
-    
-    If dest is found, return its object, otherwise return None
-    """
-    for peerObj in self.peers:
-        if peerObj.dest == dest:
-            return peerObj
-    return None
-
-</t>
-<t tx="aum.20040803134418">def putRefs(self, *args):
-    """
-    Saves one or more noderefs into filesystem
-    
-    Arguments:
-        - zero or more KPeer objects, or lists or tuples of objects
-    """
-    lst = self._expandRefsList(args)
-    for item in lst:
-        b64hash = shahash(item.dest)
-        itemPath = os.path.join(self.refsDir, b64hash)
-        itemDict = {'dest':item.dest} # might need to expand later
-        itemPickle = bencode.bencode(itemDict)
-        file(itemPath, "wb").write(itemPickle)
-    pass
-</t>
-<t tx="aum.20040803134838">@others
-</t>
-<t tx="aum.20040803134838.1">def _expandRefsList(self, args, lst=None):
-    """
-    Takes a sequence of args, each of which can be a KPeer
-    object, or a list or tuple of KPeer objects, and expands
-    this into a flat list
-    """
-    if lst == None:
-        lst = []
-    for item in args:
-        if type(item) in [type(()), type([])]:
-            self._expandRefsList(item, lst)
-        else:
-            lst.append(item)
-    return lst
-
-</t>
-<t tx="aum.20040803140601">def getRefs(self):
-    """
-    Returns a list of KPeer objects, comprising refs
-    of peers known to this node
-
-    These are read from the directory self.refsDir.
-    Any that can't be unpickled and instantiated are dropped, but logged
-    """
-    peers = []
-    for f in os.listdir(self.refsDir):
-
-        path = os.path.join(self.refsDir, f)
-        pickled = file(path, "rb").read()
-        try:
-            d = bencode.bdecode(pickled)
-        except:
-            self.log(3, "node %s, bad pickle ref file %s" % (
-                self.nodeName, f))
-            continue
-        
-        # instantiate a peer object
-        try:
-            peer = KPeer(self.node, d['dest'])
-        except:
-            self.log(3, "node %s, bad unpickled ref file %s" % (
-                self.nodeName, f))
-            continue
-
-        # success
-        peers.append(peer)
-
-    return peers
-
-</t>
-<t tx="aum.20040803141131">@others
-</t>
-<t tx="aum.20040803141131.1">def test(numnodes=10):
-
-    global n, n0, res
-
-    stasher.logVerbosity = 3
-    stasher.core = KCore(1)
-
-    os.system("rm -rf ~/.i2pkademlia")
-
-    n = KTestNetwork(10, purge=1)
-    n.connect()
-    n.start()
-    n0 = n[0]
-
-    return
-
-    if 0:
-        core.fg = True
-        n = KTestNetwork(10, purge=1)
-        n.connect()
-        n.start()
-        core.cycle()
-        
-        print "about to insert"
-        set_trace()
-        n[0].put('roses', 'red')
-        print "insert completed"
-        #set_trace()
-        
-        return
-
-    #set_trace()
-    #n[0].put('roses', 'red')
-
-    #print n[0].get('roses')
-
-    if 0:
-        successes = []
-        failures = []
-        for idx in range(numnodes):
-    
-            # build test network of known topology
-            print "Building test network"
-            n = KTestNetwork(numnodes, purge=1)
-            n.connect()
-            n.start()
-            core.n = n
-            n.trigger = 0
-
-            if 0:
-                print n[0]._findnode('roses')
-                break
-
-            if 1:
-                # store something
-                print "storing something"
-
-                n[0].put('roses', 'red')
-
-                # try to retrieve it from node i
-                print "trying to retrieve it from node %s" % idx
-                if n[idx].get('roses') != None:
-                    print "Node %s retrieved ok" % idx
-                    successes.append(idx)
-                else:
-                    print "Node %s failed to retrieve" % idx
-                    failures.append(idx)
-            
-            del n
-
-        print "Successful nodes: %s" % " ".join([str(x) for x in successes])
-        print "Failed nodes: %s" % " ".join([str(x) for x in failures])
-
-    if 0 and desperatelyDebugging:
-
-        while not n.trigger:
-            time.sleep(1)
-
-        n.trigger = 0
-        n[0].put('roses', 'red')
-        
-        while not n.trigger:
-            time.sleep(1)
-        
-        print "retrieving 'roses'"
-        print n[0].get('roses')
-
-</t>
-<t tx="aum.20040803142127">@others
-</t>
-<t tx="aum.20040803142127.1">def __str__(self):
-    return "&lt;KNode:%s=0x%s...&gt;" % (
-        self.name,
-        ("%x" % self.id.value)[:8],
-        )
-</t>
-<t tx="aum.20040803142127.2">def __repr__(self):
-    return str(self)
-
-</t>
-<t tx="aum.20040803143007">@others
-</t>
-<t tx="aum.20040803143007.1">def __str__(self):
-
-    return "&lt;KPeer:%s=&gt;0x%s... dest %s...&gt;" % (
-        self.node.name, ("%x" % self.id.value)[:8], self.dest[:8])
-
-</t>
-<t tx="aum.20040803143007.2">def __repr__(self):
-    
-    return str(self)
-
-</t>
-<t tx="aum.20040803144052">@others
-</t>
-<t tx="aum.20040803163704">def _on_ping(self, peer, msgId, **kw):
-    """
-    Handler for ping received events
-    """
-    KRpcPing(self, (peer, msgId), local=True, **kw)
-    return
-    
-    # old stuff
-
-    self.log(3, "\nnode %s\nfrom %s\nreceived:\n%s" % (self.name, peer, kw))
-
-    # randomly cause ping timeouts if testing
-    if testing:
-        howlong = random.randint(0, 5)
-        self.log(3, "deliberately pausing for %s seconds" % howlong)
-        time.sleep(howlong)
-
-    # pong back to node
-    peer.send_reply(msgId=msgId)
-
-    </t>
-<t tx="aum.20040803163704.1">def _on_unknown(self, peer, msgId, **kw):
-    """
-    Handler for unknown events
-    """
-    self.log(3, "node %s from %s received msgId=%s:\n%s" % (
-        self.name, peer, msgId, kw))
-
-</t>
-<t tx="aum.20040803193605">def _msgIdAlloc(self):
-    """
-    issue a new and unique message id
-    """
-    #self._msgIdLock.acquire()
-    msgId = self._msgIdNext
-    self._msgIdNext += 1
-    #self._msgIdLock.release()
-    return msgId
-</t>
-<t tx="aum.20040803200819">def _normalisePeer(self, peer):
-    """
-    Takes either a b64 dest string, a KPeer object or a KNode object,
-    and returns a KPeer object
-    """
-    # act according to whatever type we're given
-    if isinstance(peer, KPeer):
-        return peer # already desired format
-    elif isinstance(peer, KNode):
-        return KPeer(self, peer.dest)
-    elif isinstance(peer, str) and len(peer) &gt; 256:
-        return KPeer(self, peer)
-    else:
-        self.log(3, "node %s, trying to add invalid noderef %s" % (
-            self.name, peer))
-        raise KBadNode(peer)
-
-</t>
-<t tx="aum.20040803201812.1">def _ping(self, peer=None, callback=None, **kw):
-    """
-    Sends a ping to remote peer, and awaits response
-    
-    Not of much real use to application level, except
-    perhaps for testing
-
-    If the argument 'peer' is not given, the effect is to 'ping the
-    local node', which I guess might be a bit silly
-
-    The second argument 'callback' is a callable, which if given, makes this
-    an asynchronous (non-blocking) call, in which case the callback will be
-    invoked upon completion (or timeout).
-    
-    If the keyword 'cbArgs' is given in addition to the callback, the callback
-    will fire with the results as first argument and this value as second arg
-    """
-    if callback:
-        KRpcPing(self, callback, peer=peer, **kw)
-    else:
-        return KRpcPing(self, peer=peer).execute()
-
-</t>
-<t tx="aum.20040804001454">def distance(self, other):
-    """
-    calculates the 'distance' between this hash and another hash,
-    and returns it as i (where distance = 2^i, and 0 &lt;= i &lt; 160)
-    """
-
-    #log(4, "comparing: %s\nwith %s" % (self.value, other.value))
-
-    rawdistance = self.value ^ other.value
-    if not rawdistance:
-        return 0
-
-    return int(math.log(rawdistance, 2))
-
-</t>
-<t tx="aum.20040804013647">def _on_findNode(self, peer, msgId, **kw):
-    """
-    Handles incoming findNode command
-    """
-    KRpcFindNode(self, (peer, msgId), local=True, **kw)
-
-</t>
-<t tx="aum.20040804014115.1">def _on_findData(self, peer, msgId, **kw):
-    """
-    Handles incoming findData command
-    """
-    KRpcFindData(self, (peer, msgId), local=True, **kw)
-
-</t>
-<t tx="aum.20040804014643">def justSeen(self):
-    self.timeLastSeen = time.time()
-
-</t>
-<t tx="aum.20040804122542">class KCore(KBase):
-    """
-    Singleton class which performs all the needed background processing.
-    
-    By scheduling all processing through this object, we eliminate the
-    need to create threads on a per-node basis, and also make this thing
-    far easier to debug.
-
-    The core launches only two background threads:
-        - L{threadRxPackets} - listen for incoming packets bound for
-          any node running within a single process
-        - L{threadHousekeeping} - periodically invoke maintenance methods
-          of each node, so the node can check for timeout conditions and
-          other untoward happenings
-
-    These threads start up when the first node in this process is created,
-    and stop when the last node ceases to exist.
-
-    Upon first import, the L{stasher} module creates one instance of this
-    class. Upon creation, L{KNode} objects register themselves with this core.
-    """
-    @others
-
-</t>
-<t tx="aum.20040804122542.1">def __init__(self, bg=True):
-    """
-    Creates the I2P Kademlia core object
-    """
-    self.bg = bg
-    self.fg = False
-
-    # subscribed nodes
-    self.nodes = []
-    #self.nodesLock = threading.Lock()
-    
-    self.isRunning = False
-    self.isRunning_rx = False
-
-</t>
-<t tx="aum.20040804123117">def subscribe(self, node):
-    """
-    Called by a node to 'subscribe' for background processing
-    If this is the first node, starts the handler thread
-    """
-    #self.nodesLock.acquire()
-    try:
-        nodes = self.nodes
-        
-        if node in nodes:
-            self.log(2, "duhhh! node already subscribed" % repr(node))
-            return
-    
-        nodes.append(node)
-        
-        if not self.isRunning:
-            self.isRunning = True
-            if self.bg and not self.fg:
-                self.log(3, "First node subscribing, launching threads")
-                thread.start_new_thread(self.threadRxPackets, ())
-                thread.start_new_thread(self.threadHousekeeping, ())
-    except:
-        traceback.print_exc()
-        self.log(2, "exception")
-
-    #self.nodesLock.release()
-
-</t>
-<t tx="aum.20040804123350">def unsubscribe(self, node):
-    """
-    Unsubscribes a node from the core
-    
-    If this was the last node, stops the handler thread
-    """
-    #self.nodesLock.acquire()
-    try:
-        nodes = self.nodes
-        
-        if node not in nodes:
-            self.log(4, "duhhh! node %s was not subscribed" % repr(node))
-            return
-
-        self.log(2, "trying to unsubscribe node %s" % node.name)
-        nodes.remove(node)
-
-        if len(nodes) == 0:
-            self.isRunning = False
-    except:
-        traceback.print_exc()
-        self.log(2, "exception")
-
-    #self.nodesLock.release()
-
-</t>
-<t tx="aum.20040804123526">def threadRxPackets(self):
-    """
-    Sits on a select() loop, processing incoming datagrams
-    and actioning them appropriately.
-    """
-    self.isRunning_rx = True
-    self.log(3, "KCore packet receiver thread running")
-    try:
-        while self.isRunning:
-            socks = [node.sock for node in self.nodes]
-            if desperatelyDebugging:
-                set_trace()
-            try:
-                inlist, outlist, errlist = self.select(socks, [], [], 1)
-            except KeyboardInterrupt:
-                self.isRunning = 0
-                return
-
-            self.log(5, "\ninlist=%s" % repr(inlist))
-            if inlist:
-                self.log(5, "got one or more sockets with inbound data")
-                #self.nodesLock.acquire()
-                for sock in inlist:
-                    node = self.nodeWhichOwnsSock(sock)
-                    if node != None:
-                        node._doRx()
-                #self.nodesLock.release()
-
-            elif self.fg:
-                return
-
-            else:
-                time.sleep(0.1)
-    except:
-        #self.nodesLock.release()
-        traceback.print_exc()
-        self.log(1, "core handler thread crashed")
-    self.isRunning_rx = False
-    self.log(3, "core handler thread terminated")
-
-</t>
-<t tx="aum.20040804130347"># create an instance of _KCore
-core = KCore()
-
-</t>
-<t tx="aum.20040804130722">def __del__(self):
-    """
-    Clean up on delete
-    """
-    self.log(3, "node dying: %s" % self.name)
-
-    try:
-        del _nodes[self.name]
-    except:
-        pass
-
-    self.stop()
-
-</t>
-<t tx="aum.20040804132053">def _doRx(self):
-    """
-    Receives and handles one incoming packet
-    
-    Returns True if a packet got handled, or False if timeout
-    """
-    # get next packet
-    self.log(5, "%s seeking socket lock" % self.name)
-    #self.sockLock.acquire()
-    self.log(5, "%s got socket lock" % self.name)
-    try:
-        item = self.sock.recvfrom(-1)
-    except i2p.socket.BlockError:
-        #self.sockLock.release()
-        self.log(5, "%s released socket lock after timeout" % self.name)
-        if not runCore:
-            time.sleep(0.1)
-        return False
-    except:
-        traceback.print_exc()
-        self.log(5, "%s released socket lock after exception" % self.name)
-        #self.sockLock.release()
-        return True
-    #self.sockLock.release()
-    self.log(5, "%s released socket lock normally" % self.name)
-
-    try:
-        (data, dest) = item
-    except ValueError:
-        self.log(3, "node %s: recvfrom returned no dest, possible spoof" \
-            % self.name)
-        data = item[0]
-        dest = None
-
-    # try to decode
-    try:
-        d = messageDecode(data)
-    except:
-        traceback.print_exc()
-        self.log(3, "failed to unpickle incoming data for node %s" % \
-            self.name)
-        return True
-
-    # ditch if not a dict
-    if type(d) != type({}):
-        self.log(3, "node %s: decoded packet is not a dict" % self.name)
-        return True
-
-    # temporary workaround for sam socket bug    
-    if dest == None:
-        if hasattr(d, 'has_key') and d.has_key('dest'):
-            dest = d['dest']
-
-    # try to find it in our store
-    peerObj = self._findPeer(dest)
-    if peerObj == None:
-        # previously unknown peer - add it to our store
-        peerObj = self.addref(dest)
-    else:
-        peerObj.justSeen() # already exists - refresh its timestamp
-        self.addref(peerObj.dest)
-
-    # drop packet if no msgId
-    msgId = d.get('msgId', None)
-    if msgId == None:
-        self.log(3, "no msgId, dropping")
-        return True
-    del d['msgId']
-
-    msgType = d.get('type', 'unknown')
-
-    if desperatelyDebugging:
-        pass
-        #set_trace()
-
-    # if a local RPC is awaiting this message, fire its callback
-    item = self.rpcBindings.get((peerObj.dest, msgId), None)
-    if item:
-        rpc, peer = item
-        try:
-            rpc.unbindPeerReply(peerObj, msgId)
-            if desperatelyDebugging:
-                set_trace()
-            rpc.on_reply(peerObj, msgId, **d)
-
-        except:
-            traceback.print_exc()
-            self.log(2, "unhandled exception in RPC on_reply")
-    else:
-        # find a handler, fallback on 'unknown'
-        self.log(5, "\nnode %s\ngot msg id %s type %s:\n%s" % (
-            self.name, msgId, msgType, d))
-        hdlrName = d.get('type', 'unknown')
-        hdlr = getattr(self, "_on_"+hdlrName)
-        try:
-            if desperatelyDebugging:
-                set_trace()
-            hdlr(peerObj, msgId, **d)
-        except:
-            traceback.print_exc()
-            self.log(2, "unhandled exception in unbound packet handler %s" % hdlrName)
-
-    return True
-
-</t>
-<t tx="aum.20040804132551">def nodeWhichOwnsSock(self, sock):
-    """
-    returns ref to node which owns a socket
-    """
-    for node in self.nodes:
-        if node.sock == sock:
-            return node
-    return None
-</t>
-<t tx="aum.20040804140615">class KTestNetwork(stasher.KBase):
-    """
-    Builds and runs a variable-sized test network
-    """
-    @others
-</t>
-<t tx="aum.20040804140958">def __init__(self, numnodes=numTestNodes, doPings=True, purge=False):
-    """
-    Builds the test network
-    """
-    global Socket, select
-
-    self.trigger = 0
-
-    stasher.core.n = self    # for convenience while debugging
-
-    if purge:
-        self.purge()
-    
-    if 0:
-        if KTestNetwork.aNetworkExists:
-            raise Exception("A test network already exists, may not create another")
-        KTestNetwork.aNetworkExists = True
-
-    self.nodes = []
-
-    for i in range(numnodes):
-        nodeName = "kademlia-testnode-%s" % i
-        self.log(3, "Creating test node %s" % nodeName)
-        node = KNode(nodeName, in_depth=0, out_depth=0)
-        node.idx = i
-        node.n = self
-        self.nodes.append(node)
-        #print node.peers
-
-    self.log(3, "test network successfully created")
-
-</t>
-<t tx="aum.20040804141700">def __getitem__(self, num):
-    """
-    Allows test network to be treated as array, returns the 'num'th node
-    """
-    return self.nodes[num]
-
-</t>
-<t tx="aum.20040804141700.1">def __len__(self):
-    return len(self.nodes)
-
-</t>
-<t tx="aum.20040804141801">def start(self, doPings=True):
-    """
-    Starts up the test network
-    """
-    for node in self.nodes:
-        self.log(3, "starting node %s" % node.name)
-        node.start(doPings)
-
-</t>
-<t tx="aum.20040804141824">def stop(self):
-    """
-    Stops (or tries to stop) the test network
-    """
-    for node in self.nodes:
-        self.log(3, "stopping node %s" % node.name)
-        node.stop()
-
-</t>
-<t tx="aum.20040804142640">aNetworkExists = False
-</t>
-<t tx="aum.20040804200916">def _doHousekeeping(self):
-    """
-    Performs periodical housekeeping on this node.
-    
-    Activities include:
-        - checking pending records for timeouts
-    """
-    now = time.time()
-
-    # DEPRECATED - SWITCH TO RPC-based
-    # check for expired pings
-    for msgId, (dest, q, pingDeadline) in self.pendingPings.items():
-
-        if pingDeadline &gt; now:
-            # not timed out, leave in pending
-            continue
-
-        # ping has timed out
-        del self.pendingPings[msgId]
-        q.put(False)
-
-    # check for timed-out RPCs
-    for rpc in self.rpcPending[:]:
-        if rpc.nextTickTime != None and now &gt;= rpc.nextTickTime:
-            try:
-                rpc.on_tick()
-            except:
-                traceback.print_exc()
-                self.log(2, "unhandled exception in RPC on_tick")
-
-</t>
-<t tx="aum.20040804204524">class KPendingResultBase:
-    """
-    Class which holds the details of an RPC sent to another node.
-    """
-    @others
-
-</t>
-<t tx="aum.20040804204524.1">def __init__(self, node, typ, **kw):
-    """
-    Creates a pending result object
-    
-    Arguments:
-        - node - node which is waiting for this result
-        - typ - operation type on which we're awaiting a response,
-          one of 'ping', 'findNode', 'findData', 'store'
-    
-    Keywords:
-        - gotta think about this
-    """
-    self.node = node
-    self.typ = type
-
-    # dict of msgId, peer pairs
-    self.msgIds = {}
-
-    # indicates when to timeout and return the best available result
-    self.deadline = time.time() + timeout[typ]
-
-    # add ourself to node
-    self.node.pendingResults.append(self)
-
-</t>
-<t tx="aum.20040804205057">def _doChug(self):
-    """
-    Do what's needed to drive the node.
-    Handle incoming packets
-    Check on and action timeouts
-    """
-    # handle all available packets
-    while self._doRx():
-        pass
-
-    # do maintenance - eg processing timeouts
-    self._doHousekeeping()
-
-</t>
-<t tx="aum.20040804205740">def __cmp__(self, other):
-
-    # for sorting pending results by deadline
-    return cmp(self.deadline, other.deadline)
-
-</t>
-<t tx="aum.20040804210421">def append(self, peer, msgId):
-    """
-    Adds a (peer, msgId) pair, to watch out for
-    """
-    peer = self.node._normalisePeer(peer)
-
-    self.msgIds[msgId] = peer
-    self.node.awaitingMsgIds[msgId] = self
-
-</t>
-<t tx="aum.20040804210911">def wait(self):
-    """
-    Called by application-level routines to wait on and return some kind of result
-    """
-    return self.queue.get()
-</t>
-<t tx="aum.20040804211058">def check(self, now=None):
-    """
-    Checks for a timeout condition, which if one occurs, sticks the best
-    available result onto the queue to be picked up by the caller
-    """
-    if now == None:
-        now = time.time()
-    if now &gt; self.deadline:
-        self.queue.put(self.bestAvailableResult())
-
-</t>
-<t tx="aum.20040804211248">def on_tick(self):
-    """
-    Override this in subclasses.
-    If a timeout occurs, this routine gets called, and should return
-    the 'best available result' to be delivered back to synchronous caller
-    """
-    raise KNotImplemented
-</t>
-<t tx="aum.20040804211539">def on_packet(self, msgId, **details):
-    """
-    Called when a packet of id msgId arrives
-
-    Should return True if this packet was for us, or False if not
-    """
-    raise KNotImplemented
-
-</t>
-<t tx="aum.20040804212126">class KPendingResultPing(KPendingResultBase):
-    """
-    for managing synchronous pings
-    """
-    @others
-</t>
-<t tx="aum.20040804212126.1">def __init__(self, node):
-
-    KPendingResultBase.__init__(self, node, 'ping')
-
-</t>
-<t tx="aum.20040804212342">def on_tick(self):
-    """
-    Handle synchronous ping timeouts
-    """
-    return False
-
-</t>
-<t tx="aum.20040804212607">def on_packet(self, msgId, **details):
-    """
-    Must have got back a ping reply
-    """
-    self.destroySelf()
-    self.queue.put(True)
-
-    return True
-</t>
-<t tx="aum.20040804212714">def destroySelf(self):
-    """
-    Remove ourself from node
-    """
-    self.node.pendingResults.remove(self)
-
-
-</t>
-<t tx="aum.20040804213616">def __eq__(self, other):
-
-    #self.log(2, "KPeer: comparing %s to %s (%s to %s)" % (self, other, self.__class__, other.__class__))
-    res = self.id == getattr(other, 'id', None)
-    #self.log(2, "KPeer: res=%s" % res)
-    return res
-
-</t>
-<t tx="aum.20040804220932">def __ne__(self, other):
-    return not (self == other)
-</t>
-<t tx="aum.20040804223241"></t>
-<t tx="aum.20040805001258">def execute(self):
-    """
-    Only for synchronous (application-level) execution.
-    Wait for the RPC to complete (or time out) and return
-    whatever it came up with
-    """
-    if core.fg:
-        print "servicing background thread"
-        while self.queue.empty():
-            core.cycle()
-
-    return self.queue.get()
-
-</t>
-<t tx="aum.20040805001449"></t>
-<t tx="aum.20040805001742">def bindPeerReply(self, peer, msgId):
-    """
-    Sets up the node to give us a callback when a reply
-    comes in from downstream peer 'peer' with msg id 'msgId'
-    """
-    self.localNode.rpcBindings[(peer.dest, msgId)] = (self, peer)
-
-</t>
-<t tx="aum.20040805001926">def unbindPeerReply(self, peer, msgId):
-    """
-    Disables the callback from node for replies
-    from peer 'peer' with msgId 'msgId'
-    """
-    bindings = self.localNode.rpcBindings
-    peerdest = peer.dest
-    if bindings.has_key((peerdest, msgId)):
-        del bindings[(peerdest, msgId)]
-
-</t>
-<t tx="aum.20040805004949">def unbindAll(self):
-    """
-    Remove all reply bindings
-    """
-    bindings = self.localNode.rpcBindings
-    self.log(5, "node bindings before: %s" % bindings)
-    for k,v in bindings.items():
-        if v[0] == self:
-            del bindings[k]
-    self.log(5, "node bindings after: %s" % bindings)
-
-</t>
-<t tx="aum.20040805012557">class KRpc(KBase):
-    """
-    Base class for RPCs between nodes.
-    Refer subclasses
-    """
-    @others
-</t>
-<t tx="aum.20040805012557.1">def __init__(self, localNode, client=None, **kw):
-    """
-    Holds all the information for an RPC
-
-    Arguments:
-        - localNode - the node from which this RPC is being driven
-        - client - a representation of who is initiating this rpc, one of:
-            - None - an API caller, which is to be blocked until the RPC completes
-              or times out
-            - (upstreamPeer, upstreamMsgId) - an upstream peer
-            - callable object - something which requires a callback upon completion
-              in which case the callable will be invoked with the RPC results as the
-              first argument
-
-    Keywords:
-        - cbArgs - optional - if given, and if client is a callback, the callback
-          will be invoked with the results as first argument, and this object as
-          second argument
-    """
-    self.localNode = localNode
-
-    if client == None:
-        # an api client
-        self.isLocal = True
-        self.queue = Queue.Queue()
-        self.callback = None
-    elif callable(client):
-        self.isLocal = False
-        self.callback = client
-    elif isinstance(client, tuple):
-        # we're doing the RPC on behalf of an upstream peer
-        upstreamPeer, upstreamMsgId = client
-        upstreamPeer = localNode._normalisePeer(upstreamPeer)
-        self.isLocal = False
-        self.upstreamPeer = upstreamPeer
-        self.upstreamMsgId = upstreamMsgId
-        self.callback = None
-
-    # save keywords
-    self.__dict__.update(kw)
-
-    # set time for receiving a tick.
-    # if this is set to an int absolute time value, the on_tick method
-    # will be called as soon as possible after that time
-    self.nextTickTime = None
-
-    # and register with node as a pending command
-    self.localNode.rpcPending.append(self)
-
-    # now start up the request
-    self.start()
-
-</t>
-<t tx="aum.20040805013630">def start(self):
-    """
-    Start the RPC running.
-    Override this in subclasses
-    """
-    raise KNotImplemented
-
-</t>
-<t tx="aum.20040805013903">def on_reply(self, peer, msgId, **details):
-    """
-    Callback which fires when a downstream peer replies
-    
-    Override this in subclasses
-    """
-    raise KNotImplemented
-
-</t>
-<t tx="aum.20040805013957">def on_tick(self):
-    """
-    Callback which fires if the whole RPC times out, in which
-    case the RPC should return whatever it can
-    
-    Override in subclasses
-    """
-    self.localNode.rpcPending.remove(self)
-
-</t>
-<t tx="aum.20040805014353">class KRpcPing(KRpc):
-    """
-    Implements the PING rpc as per Kademlia spec
-    """
-    @others
-</t>
-<t tx="aum.20040805014353.1">def __init__(self, localNode, client=None, **kw):
-    """
-    Creates and performs a PING RPC
-
-    Arguments:
-        - localNode - the node performing this RPC
-        - upstreamPeer - if given, the peer wanting a reply
-        - upstreamMsgId - if upstreamPeer is given, this is the msgId
-          of the RPC message from the upstream peer
-
-    Keywords:
-        - peer - the peer to ping - default is local node
-    """
-    peer = kw.get('peer', None)
-    if peer != None:
-        peer = localNode._normalisePeer(peer)
-    self.peerToPing = peer
-
-    if kw.has_key('cbArgs'):
-        KRpc.__init__(self, localNode, client, cbArgs=kw['cbArgs'])
-    else:
-        KRpc.__init__(self, localNode, client)
-
-</t>
-<t tx="aum.20040805014900">def start(self):
-    """
-    Sends out the ping
-    """
-    peer = self.peerToPing
-
-    # are we ourselves being pinged?
-    if peer == None:
-        # yes, just reply
-        self.returnValue(True)
-        return
-
-    # no - we need to ping a peer
-    thisNode = self.localNode
-
-    msgId = thisNode.msgId = thisNode._msgIdAlloc()
-
-    # bind for peer response
-    self.bindPeerReply(peer, msgId)
-
-    # and send it off
-    self.log(3, "node %s sending ping" % self.localNode.name)
-    peer.send_ping(msgId=msgId)
-
-    # and set a reply timeout
-    self.nextTickTime = time.time() + timeout['ping']
-
-</t>
-<t tx="aum.20040805014900.1">def on_reply(self, peer, msgId, **details):
-    """
-    Callback for PING reply
-    """
-    self.log(3, "got ping reply from %s" % peer)
-    self.returnValue(True)
-
-</t>
-<t tx="aum.20040805030707">def terminate(self):
-    """
-    Clean up after ourselves.
-    Mainly involves removing ourself from local node
-    """
-    self.unbindAll()
-    try:
-        self.localNode.rpcPending.remove(self)
-    except:
-        #traceback.print_exc()
-        pass
-
-</t>
-<t tx="aum.20040805031135">def messageEncode(params):
-    """
-    Serialise the dict 'params' for sending
-    
-    Temporarily using bencode - replace later with a more
-    efficient struct-based impl.
-    """
-    try:
-        return bencode.bencode(params)
-    except:
-        log(1, "encoder failed to encode: %s" % repr(params))
-        raise
-
-</t>
-<t tx="aum.20040805031135.1">def messageDecode(raw):
-    return bencode.bdecode(raw)
-</t>
-<t tx="aum.20040805032351">def on_tick(self):
-    """
-    'tick' handler.
-    
-    For PING RPC, the only time we should get a tick is when the ping
-    has timed out
-    """
-    self.log(3, "timeout awaiting ping reply from %s" % self.peerToPing)
-    self.returnValue(False)
-
-</t>
-<t tx="aum.20040805040237">def _on_reply(self, peer, msgId, **kw):
-    """
-    This should never happen
-    """
-    self.log(4, "got unhandled reply:\npeer=%s\nmsgId=%s\nkw=%s" % (
-        peer, msgId, kw))
-
-</t>
-<t tx="aum.20040805040409">def send_reply(self, **kw):
-    """
-    Sends an RPC reply back to upstream peer
-    """
-    self.log(5, "\nnode %s\nreplying to peer %s:\n%s" % (
-        self.node, self, kw))
-    self.send_raw(type="reply", **kw)
-
-</t>
-<t tx="aum.20040805123632">def threadHousekeeping(self):
-    """
-    Periodically invoke nodes' housekeeping
-    """
-    self.log(3, "\nnode housekeeping thread running")
-    try:
-        while self.isRunning:
-            #self.log(4, "calling nodes' housekeeping methods")
-            #self.nodesLock.acquire()
-            for node in self.nodes:
-                node._doHousekeeping()
-            #self.nodesLock.release()
-            time.sleep(1)
-        self.log(3, "\nnode housekeeping thread terminated")
-    except:
-        #self.nodesLock.release()
-        traceback.print_exc()
-        self.log(1, "\nnode housekeeping thread crashed")
-
-</t>
-<t tx="aum.20040805130159">class KRpcFindNode(KRpc):
-    """
-    Implements the FIND_NODE rpc as per Kademlia spec
-    """
-    @others
-</t>
-<t tx="aum.20040805130514">def __init__(self, localNode, client=None, **kw):
-    """
-    Creates and launches the findNode rpc
-    
-    Arguments:
-        - localNode - the node performing this RPC
-        - client - see KRpc.__init__
-
-    Keywords:
-        - hash - a string, long int or KHash object representing
-          what we're looking for. treatment depends on type:
-              - KHash object - used as is
-              - string - gets wrapped into a KHash object
-              - long int - wrapped into a KHash object
-          refer KHash.__init__
-        - raw - whether 'hash' is already a hash, default True
-        - local - True/False - whether to only search local store,
-          or pass on the query to the network, default True
-    """
-    kw = dict(kw)
-    if kw.get('raw', False):
-        h = kw['hash']
-        del kw['hash']
-        kw['raw'] = h
-        self.hashWanted = KHash(**kw)
-    else:
-        self.hashWanted = KHash(kw['hash'], **kw)
-    self.isLocalOnly = kw.get('local', True)
-
-    self.numQueriesPending = 0
-
-    self.numRounds = 0   # count number of rounds
-    self.numReplies = 0  # number of query replies received
-    self.numQueriesSent = 0
-    self.numPeersRecommended = 0
-
-    # whichever mode we're called from, we gotta find the k closest peers
-    self.localNode = localNode
-    self.peerTab = self.findClosestPeersInitial()
-
-    self.log(4, "KRpcFindNode: isLocalOnly=%s" % self.isLocalOnly)
-
-    if kw.has_key('cbArgs'):
-        KRpc.__init__(self, localNode, client, cbArgs=kw['cbArgs'])
-    else:
-        KRpc.__init__(self, localNode, client)
-
-</t>
-<t tx="aum.20040805131021.1">@
-Tech overview:
-    - this implementation creates each Node ID as an SHA1 hash of
-      the node's 'destination' - the string which constitutes its
-      address as an I2P endpoint.
-
-Datagram formats:
-    - each datagram sent from one node to another is a python dict object,
-      encoded and decoded with the 'bencode' object serialisation module.
-    - we use bencode because regular Python pickle is highly insecure,
-      allowing crackers to create malformed pickles which can have all
-      manner of detrimental effects, including execution of arbitrary code.
-    - the possible messages are listed below, along with their consituent
-      dictionary keys:
-          1. ping:
-              - msgId - a message identifier guaranteed to be unique
-                with respect to the sending node
-          2. findNode:
-              - msgId - unique message identifier
-              - hash - the hash we're looking for
-              - initiator - True/False, according to whether this node
-                should initiate/perform the findNode, or whether this
-                rpc is coming from another seeking node
-          3. findData:
-              - msgId - unique message identifier
-              - hash - the exact key hash of the data we want to retrieve
-              - initiator - True/False, according to whether this node
-                should initiate/perform the findNode, or whether this
-                rpc is coming from another seeking node
-          4. store:
-              - msgId - unique message identifier
-              - hash - the exact key hash of the data we want to store
-              - data - the data we want to store
-          5. reply:
-              - msgId - the original msgId we're replying to
-             The other items in a reply message depend on what kind
-             of message we're replying to, listed below:
-                    1. ping - no additional data
-                    2. findNode:
-                        - nodes - a list of dests nearest the given hash
-                    3. findData:
-                        - nodes - as for findNode, OR
-                        - data - the retrieved data, or None if not found
-                    4. store:
-                        - status - True or False according to whether
-                          the store operation was successful
-</t>
-<t tx="aum.20040805140236">def _findnode(self, something=None, callback=None, **kw):
-    """
-    Mainly for testing - does a findNode query on the network
-    
-    Arguments:
-        - something - one of:
-            - plain string - the string gets hashed and used for the search
-            - int or long int - this gets used as the raw hash
-            - a KHash object - that's what gets used
-            - None - the value of the 'raw' keyword will be used instead
-        - callback - optional - if given, a callable object which will be
-          called upon completion, with the result as argument
-
-    Keywords:
-        - local - optional - if True, only returns the closest peers known to
-          node. if False, causes node to query other nodes.
-          default is False
-        - raw - one of:
-            - 20-byte string - this gets used as a binary hash
-            - 40-byte string - this gets used as a hex hash
-    """
-    if not kw.has_key('local'):
-        kw = dict(kw)
-        kw['local'] = False
-
-    self.log(3, "about to instantiate findnode rpc")
-    if callback:
-        KRpcFindNode(self, callback, hash=something, **kw)
-        self.log(3, "asynchronously invoked findnode, expecting callback")
-    else:
-        lst = KRpcFindNode(self, hash=something, **kw).execute()
-        self.log(3, "back from findnode rpc")
-        res = [self._normalisePeer(p) for p in lst] # wrap in KPeer objects
-        return res
-
-</t>
-<t tx="aum.20040805140416">def start(self):
-    """
-    Kicks off this RPC
-    """
-    # if we're being called by an upstream initiator, just return the peer list
-    if self.isLocalOnly:
-        peerDests = [peer.dest for peer in self.peerTab]
-        self.log(5, "findNode: local only: returning to upstream with %s" % repr(peerDests))
-        self.returnValue(peerDests)
-        return
-
-    # just return nothing if we don't have any peers
-    if len(self.peerTab) == 0:
-        self.returnValue([])
-        return
-
-    # send off first round of queries
-    self.sendSomeQueries()
-
-    return
-
-</t>
-<t tx="aum.20040805140632">def on_reply(self, peer, msgId, **details):
-    """
-    Callback for FIND_NODE reply
-    """
-    # shorthand
-    peerTab = self.peerTab
-
-    self.numReplies += 1
-
-    # ------------------------------------------------------------
-    # determine who replied, and get the raw dests sent back
-    try:
-        peerRec = peerTab[peer]
-    except:
-        traceback.print_exc()
-        self.log(3, "discarding findNode reply from unknown peer %s %s, discarding" % (
-            peer, details))
-        return
-
-    # one less query to wait for
-    self.numQueriesPending -= 1
-
-    # ----------------------------------------------------------
-    # peerRec is the peer that replied
-    # peers is a list of raw dests
-
-    # save ref to this peer, it's seemingly good
-    self.localNode.addref(peerRec.peer)
-
-    # mark it as having replied
-    if peerRec.state != 'queried':
-        self.log(2, "too weird - got a reply from a peer we didn't query")
-    peerRec.state = 'replied'
-
-    # wrap the returned peers as KPeer objects
-    peersReturned = details.get('result', [])
-    peersReturned = [self.localNode._normalisePeer(p) for p in peersReturned]
-
-    self.numPeersRecommended += len(peersReturned)
-
-    # and add them to table in state 'recommended'
-    for p in peersReturned:
-        peerTab.append(p, 'recommended')
-
-    # try to fire off more queries
-    self.sendSomeQueries()
-
-    # and check for and action possible end of query round
-    self.checkEndOfRound()
-
-
-</t>
-<t tx="aum.20040805141509">def on_tick(self):
-    """
-    Callback for FIND_NODE reply timeout
-    """
-    # check for timeouts, and update offending peers
-    now = time.time()
-    for peerRec in self.peerTab:
-        if peerRec.hasTimedOut(now):
-            peerRec.state = 'timeout'
-
-            # makes room for more queries
-            self.sendSomeQueries()
-
-    # possible end of round
-    self.checkEndOfRound()
-
-    # schedule next tick
-    self.nextTickTime = time.time() + 5
-
-</t>
-<t tx="aum.20040805143215">@
-Verbatim extract from original Kademlia paper follows:
-
-The lookup initiator starts by picking x nodes from its closest
-non-empty k-bucket (or, if that bucket has fewer than x
-entries, it just takes the closest x nodes it knows of).
-
-The initiator then sends parallel, asynchronous
-FIND NODE RPCs to the x nodes it has chosen.
-x is a system-wide concurrency parameter, such as 3.
-
-In the recursive step, the initiator resends the
-FIND NODE to nodes it has learned about from previous RPCs.
-
-[Paraphrased - in the recursive step, the initiator sends a FIND_NODE to
-each of the nodes that were returned as results of these previous
-FIND_NODE RPCs.]
-
-(This recursion can begin before all of the previous RPCs have
-returned).
-
-Of the k nodes the initiator has heard of closest to
-the target, it picks x that it has not yet queried and resends
-the FIND_NODE RPC to them.
-
-Nodes that fail to respond quickly are removed from consideration
-until and unless they do respond.
-
-If a round of FIND_NODEs fails to return a node any closer
-than the closest already seen, the initiator resends
-the FIND NODE to all of the k closest nodes it has
-not already queried.
-
-The lookup terminates when the initiator has queried and gotten
-responses from the k closest nodes it has seen.
-</t>
-<t tx="aum.20040805153146">class KRpcFindData(KRpcFindNode):
-    """
-    variant of KRpcFindNode which returns key value if found
-    """
-    @others
-
-</t>
-<t tx="aum.20040805153315">class KRpcStore(KRpc):
-    """
-    Implements key storage
-    """
-    @others
-</t>
-<t tx="aum.20040805153555">def _finddata(self, something=None, callback=None, **kw):
-    """
-    As for findnode, but if data is found, return the data instead
-    """
-    if not kw.has_key('local'):
-        kw = dict(kw)
-        kw['local'] = False
-
-    self.log(3, "about to instantiate finddata rpc")
-    if callback:
-        KRpcFindData(self, callback, hash=something, **kw)
-        self.log(3, "asynchronously invoked finddata, expecting callback")
-    else:
-        res = KRpcFindData(self, hash=something, **kw).execute()
-        self.log(3, "back from finddata rpc")
-        if not isinstance(res, str):
-            self.log(4, "findData RPC returned %s" % repr(res))
-            res = [self._normalisePeer(p) for p in res] # wrap in KPeer objects
-        return res
-
-</t>
-<t tx="aum.20040805153555.1">def _store(self, key, value, callback=None, **kw):
-    """
-    Performs a STORE rpc
-    
-    Arguments:
-        - key - string - text name of key
-        - value - string - value to store
-
-    Keywords:
-        - local - if given and true, only store value onto local store
-    """
-    if not kw.has_key('local'):
-        kw = dict(kw)
-        kw['local'] = False
-
-    key = shahash(key)
-    if callback:
-        KRpcStore(self, callback, key=key, value=value, **kw)
-        self.log(3, "asynchronously invoked findnode, expecting callback")
-    else:
-        res = KRpcStore(self, key=key, value=value, **kw).execute()
-        return res
-
-</t>
-<t tx="aum.20040805154232">type = 'unknown' # override in subclass
-
-</t>
-<t tx="aum.20040805154253">type = 'ping'
-
-</t>
-<t tx="aum.20040805154306">type = 'findNode'
-</t>
-<t tx="aum.20040805154321">type = 'findData'
-</t>
-<t tx="aum.20040805154344">type = 'store'
-</t>
-<t tx="aum.20040805155412">@ignore
-@language python
-"""
-Bloom filters in Python
-Adam Langley &lt;agl@imperialviolet.org&gt;
-"""
-@others
-</t>
-<t tx="aum.20040805155412.1">import array
-import struct
-
-</t>
-<t tx="aum.20040805155412.2">__all__ = ['Bloom']
-
-mixarray = array.array ('B', '\x00' * 256)
-# The mixarray is based on RC4 and used as diffusion in the hashing function
-
-</t>
-<t tx="aum.20040805155412.3">def mixarray_init (mixarray):
-  for i in range (256):
-    mixarray[i] = i
-  k = 7
-  for j in range (4):
-    for i in range (256):
-      s = mixarray[i]
-      k = (k + s) % 256
-      mixarray[i] = mixarray[k]
-      mixarray[k] = s
-
-mixarray_init(mixarray)
-
-</t>
-<t tx="aum.20040805155412.4">class Bloom (object):
-  """
-  Bloom filters provide a fast and compact way of checking set membership.
-  They do this by introducing a risk of a false positive (but there are
-  no false negatives).
-
-  For more information see:
-      - http://www.cs.wisc.edu/~cao/papers/summary-cache/node8.html
-  """
-  @others
-
-</t>
-<t tx="aum.20040805155412.5">def __init__ (self, bytes, hashes):
-    '''
-    bytes is the size of the bloom filter in 8-bit bytes and
-    hashes is the number of hash functions to use.
-    Consult the web page linked above for values to use.
-    If in doubt, bytes = num_elements and hashes = 4
-    '''
-    self.hashes = hashes
-    self.bytes  = bytes
-
-    self.a = self._make_array (bytes)
-
-</t>
-<t tx="aum.20040805155412.6">def _make_array (self, size):
-
-    a = array.array ('B')
-    # stupidly, there's no good way that I can see of
-    # resizing an array without allocing a huge string to do so
-    # thus I use this, slightly odd, method:
-    blocklen = 256
-    arrayblock = array.array ('B', '\x00' * blocklen)
-    todo = size
-    while (todo &gt;= blocklen):
-        a.extend (arrayblock)
-        todo -= blocklen
-    if todo:
-        a.extend (array.array ('B', '\x00' * todo))
-
-    # now a is of the right length
-    return a
-
-</t>
-<t tx="aum.20040805155412.7">def _hashfunc (self, n, val):
-    '''Apply the nth hash function'''
-
-    global mixarray
-      
-    b = [ord(x) for x in struct.pack ('I', val)]
-    c = array.array ('B', [0, 0, 0, 0])
-    for i in range (4):
-        c[i] = mixarray[(b[i] + n) % 256]
-    
-    return struct.unpack ('I', c.tostring())[0]
-
-</t>
-<t tx="aum.20040805155412.8">def insert(self, val):
-
-    for i in range(self.hashes):
-        n = self._hashfunc(i, val) % (self.bytes * 8)
-        self.a[n // 8] |= self.bitmask[n % 8]
-
-</t>
-<t tx="aum.20040805155412.9">def __contains__ (self, val):
-
-    for i in range (self.hashes):
-        n = self._hashfunc (i, val) % (self.bytes * 8)
-        if not self.a[n // 8] &amp; self.bitmask[n % 8]:
-            return 0
-
-    return 1
-
-</t>
-<t tx="aum.20040805155412.10">class CountedBloom (Bloom):
-    """
-    Just like a Bloom filter, but provides counting (e.g. you can delete as well).
-    This uses 4 bits per bucket, so is generally four times larger
-    than the same non-counted bloom filter.
-    """
-    @others
-</t>
-<t tx="aum.20040805155412.11">def __init__ (self, buckets, hashes):
-    '''
-    Please note that @buckets must be even.
-    Also note that with a Bloom object you give the
-    number of *bytes* and each byte is 8 buckets.
-    Here you're giving the number of buckets.
-    '''
-    assert buckets % 2 == 0
-
-    self.hashes = hashes
-    self.buckets = buckets
-
-    self.a = self._make_array (buckets // 2)
-
-</t>
-<t tx="aum.20040805155412.12">def insert (self, val):
-
-    masks  = [(0x0f, 0xf0), (0xf0, 0x0f)]
-    shifts = [4,            0           ]
-
-    for i in range (self.hashes):
-        n = self._hashfunc (i, val) % self.buckets
-        byte   = n // 2
-        bucket = n % 2
-        (notmask, mask) = masks[bucket]
-        shift  = shifts[bucket]
-        bval    = ((self.a[byte] &amp; mask) &gt;&gt; shift)
-        if bval &lt; 15:
-            # we shouldn't increment it if it's at the maximum
-            bval += 1
-        self.a[byte] = (self.a[byte] &amp; notmask) | (bval &lt;&lt; shift)
-
-</t>
-<t tx="aum.20040805155412.13">def __contains__ (self, val):
-
-    masks  = [(0x0f, 0xf0), (0xf0, 0x0f)]
-    shifts = [4,            0           ]
-    
-    for i in range (self.hashes):
-        n = self._hashfunc (i, val) % self.buckets
-        byte   = n // 2
-        bucket = n % 2
-        (notmask, mask) = masks[bucket]
-        shift  = shifts[bucket]
-        bval    = ((self.a[byte] &amp; mask) &gt;&gt; shift)
-    
-        if bval == 0:
-            return 0
-
-    return 1
-    </t>
-<t tx="aum.20040805155412.14">def __delitem__ (self, val):
-
-    masks  = [(0x0f, 0xf0), (0xf0, 0x0f)]
-    shifts = [4,            0           ]
-    
-    for i in range (self.hashes):
-        n = self._hashfunc (i, val) % self.buckets
-        byte   = n // 2
-        bucket = n % 2
-        (notmask, mask) = masks[bucket]
-        shift  = shifts[bucket]
-        bval    = ((self.a[byte] &amp; mask) &gt;&gt; shift)
-    
-        if bval &lt; 15: # we shouldn't decrement it if it's at the maximum
-            bval -= 1
-        
-        self.a[byte] = (self.a[byte] &amp; notmask) | (bval &lt;&lt; shift)
-
-</t>
-<t tx="aum.20040805155412.15">if __name__ == '__main__':
-
-    print 'Testing bloom filter: there should be no assertion failures'
-    a = Bloom (3, 4)
-    
-    a.insert (45)
-    print a.a
-    a.insert (17)
-    print a.a
-    a.insert (12)
-    print a.a
-    assert 45 in a
-    
-    assert 45 in a
-    assert not 33 in a
-    assert 45 in a
-    assert 17 in a
-    assert 12 in a
-    
-    c = 0
-    for x in range (255):
-        if x in a:
-            c += 1
-    print c
-    print float(c)/255
-    
-    
-    a = CountedBloom (24, 4)
-    a.insert (45)
-    print a.a
-    a.insert (17)
-    print a.a
-    a.insert (12)
-    print a.a
-    assert 45 in a
-    
-    assert 45 in a
-    assert not 33 in a
-    assert 45 in a
-    assert 17 in a
-    assert 12 in a
-    
-    c = 0
-    for x in range (255):
-        if x in a:
-            c += 1
-    print c
-    print float(c)/255
-    
-    del a[45]
-    assert not 45 in a
-
-</t>
-<t tx="aum.20040805160344">bitmask = [0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01]
-
-</t>
-<t tx="aum.20040805182736">def __iter__(self):
-    return iter(self.nodes)
-</t>
-<t tx="aum.20040805185215">def findClosestPeersInitial(self):
-    """
-    Searches our k-buckets, and returns a table of k of
-    peers closest to wanted hash into self.closestPeersInitial
-    """
-    hashobj = self.hashWanted
-
-    lst = []
-    buckets = self.localNode.buckets
-    for bucket in buckets:
-        for peer in bucket:
-            lst.append(peer)
-
-    table = KPeerQueryTable(lst, self.hashWanted, 'start')
-    table.sort()
-
-    return table[:maxBucketSize]
-
-</t>
-<t tx="aum.20040805185902">def returnValue(self, res=None, **kw):
-    """
-    Passes a return value back to the original caller, be it
-    the local application, or an upstream peer
-    
-    Arguments:
-        - just one - a result object to pass back, if this RPC
-          was instigated by a local application call.
-          Note that if this RPC was instigated by an upstream
-          peer, this will be ignored.
-    
-    Keywords:
-        - the items to return, in the case that this RPC was
-          instigated by an upstream peer. Ignored if this
-          RPC was instigated by a local application call.
-          Note - the RPC invocation/reply dict keys are
-          listed at the top of this source file.
-    """
-    self.terminate()
-    if self.callback:
-        if hasattr(self, 'cbArgs'):
-            self.callback(res, self.cbArgs)
-        else:
-            self.callback(res)
-    elif self.isLocal:
-        self.queue.put(res)
-    else:
-        self.upstreamPeer.send_reply(msgId=self.upstreamMsgId,
-                                     **kw)
-</t>
-<t tx="aum.20040806002319">def addPeerIfCloser(self, peer):
-    """
-    Maintains the private .peersToQuery array.
-    If the array is not yet maxed (ie, length &lt; maxBucketSize),
-    the peer is simply added.
-    However, if the array is maxed, it finds the least-close peer,
-    and replaces it with the given peer if closer.
-    """
-</t>
-<t tx="aum.20040806132808">def isCloserThanQueried(self, peer):
-    """
-    Test function which returns True if argument 'peer'
-    is closer than all the peers in self.peersAlreadyQueried,
-    or False if not
-    """
-    for p in self.peersAlreadyQueried:
-        if p.id.rawdistance(self.hashWanted) &lt; peer.id.rawdistance(self.hashWanted):
-            return False
-    return True
-
-</t>
-<t tx="aum.20040806144708">def __del__(self):
-
-    #self.log(4, "\nRPC %s getting the chop" % (str(self)))
-    pass
-
-</t>
-<t tx="aum.20040806144812">def __str__(self):
-
-    return "&lt;%s on node %s&gt;" % (self.__class__.__name__, self.localNode.name)
-
-</t>
-<t tx="aum.20040806144829">def __repr__(self):
-    return str(self)
-</t>
-<t tx="aum.20040806223556">class KPeerQueryRecord(KBase):
-    """
-    Keeps state information regarding a peer we're quering
-    """
-    @others
-</t>
-<t tx="aum.20040806223857">class KPeerQueryTable(KBase):
-    """
-    Holds zero or more instances of KPeerQuery and
-    presents/sorts table in different forms
-    """
-    @others
-</t>
-<t tx="aum.20040806232701">def __str__(self):
-    return "&lt;KTestNetwork: %d nodes&gt;" % len(self.nodes)
-</t>
-<t tx="aum.20040806232714">def __repr__(self):
-    return str(self)
-
-</t>
-<t tx="aum.20040806234241">def sendSomeQueries(self, **kw):
-    """
-    First step of findNode
-    
-    Select alpha nodes that we haven't yet queried, and send them queries
-    """
-    # bail if too busy
-    if self.numQueriesPending &gt;= maxConcurrentQueries:
-        return
-
-    # shorthand
-    localNode = self.localNode
-    hashWanted = self.hashWanted
-
-    # randomly choose some peers
-    #somePeerRecs = self.peerTab.chooseN(numSearchPeers)
-    somePeerRecs = self.peerTab.select('start')
-    
-    # start our ticker
-    self.nextTickTime = time.time() + timeout['findNode']
-
-    numQueriesSent = 0
-
-    # and send them findNode queries
-    if len(somePeerRecs) &gt; 0:
-        for peerRec in somePeerRecs:
-            self.log(3, "querying %s" % peerRec)
-            if self.numQueriesPending &lt; maxConcurrentQueries:
-                self.sendOneQuery(peerRec)
-                numQueriesSent += 1
-            else:
-                break
-        self.log(3, "%s queries sent, awaiting reply" % numQueriesSent)
-    else:
-        self.log(3, "no peer recs???")
-        for peerRec in self.peerTab:
-            self.log(4, "%s state=%s, dest=%s..." % (peerRec, peerRec.state, peerRec.dest[:12]))
-
-</t>
-<t tx="aum.20040807003220">def returnTheBestWeGot(self):
-    """
-    Returns the k closest nodes to the wanted hash that we have
-    actually heard from
-    """
-    # pick the peers which have replied to us
-    closest = self.peerTab.select('closest')
-    
-    self.peerTab.dump()
-
-    # add ourself to the list - we could easily be one of the best
-    localNode = self.localNode
-    selfDest = localNode._normalisePeer(localNode.dest) 
-    closest.append(selfDest, state='closest')
-    
-    # sort in order of least distance first
-    closest.sort()
-
-    # pick the best k of these
-    #peersHeardFrom = peersHeardFrom[:maxBucketSize]
-    #peersHeardFrom = peersHeardFrom[:numSearchPeers]
-
-    # extract their dest strings
-    peers = [p.peer.dest for p in closest]
-
-    # pass these back
-    self.returnValue(peers)
-
-    # and we're done
-    return
-
-</t>
-<t tx="aum.20040807004327">def __init__(self, lst=None, sorthash=None, state=None, **kw):
-    self.peers = []
-    if lst == None:
-        lst = []
-    else:
-        self.setlist(lst, state, **kw)
-    self.sorthash = sorthash
-
-</t>
-<t tx="aum.20040807004327.1">def setlist(self, lst, state=None, **kw):
-    for item in lst:
-        self.append(item, state, **kw)
-
-</t>
-<t tx="aum.20040807004327.2">def extend(self, items, state, **kw):
-    for item in items:
-        self.append(item, state, **kw)
-
-</t>
-<t tx="aum.20040807004327.3">def append(self, item, state=None, **kw):
-
-    if isinstance(item, KPeerQueryRecord):
-        self.log(5, "adding a KPeerQueryRecord, state=%s" % state)
-        if state != None:
-            item.state = state
-        item.__dict__.update(kw)
-        peerRec = item
-
-    elif isinstance(item, KPeer):
-        self.log(5, "adding a KPeer")
-        peerRec = KPeerQueryRecord(item, self, state, **kw)
-
-    else:
-        self.log(2, "bad peer %s" % repr(item))
-        raise KBadPeer
-
-    if peerRec not in self:
-        self.log(5, "peerRec=%s list=%s" % (peerRec, self.peers))
-        self.peers.append(peerRec)
-    else:
-        self.log(5, "trying to append duplicate peer???")
-
-</t>
-<t tx="aum.20040807004327.4">def remove(self, item):
-    self.peers.remove(item)
-
-</t>
-<t tx="aum.20040807004327.5">def getExpired(self):
-    """
-    return a list of peers which have expired
-    """
-    return KPeerQueryTable(
-            filter(lambda item: item.hasTimedOut(), self.peers),
-            self.sorthash
-            )
-
-</t>
-<t tx="aum.20040807004327.6">def purgeExpired(self):
-    """
-    Eliminate peers which have expired
-    """
-    for peer in self.peers:
-        if peer.hasTimedOut():
-            self.peers.remove(peer)
-
-</t>
-<t tx="aum.20040807004327.7">def __getitem__(self, idx):
-    """
-    Allow the table to be indexed by any of:
-        - KPeerQueryRecord
-        - integer index
-        - long string - treated as dest
-        - short string - treated as peer id hash string
-        - KHash - finds peer with that id
-        - KPeer - returns peer with that peer
-    """
-    if type(idx) == type(0):
-        return self.peers[idx]
-    elif isinstance(idx, KPeer):
-        for peer in self.peers:
-            if peer.peer == idx:
-                return peer
-        raise IndexError("Query table has no peer %s" % idx)
-    elif isinstance(idx, str):
-        if len(str) &gt; 512:
-            for peer in self.peers:
-                if peer.peer.dest == idx:
-                    return peer
-            raise IndexError("No peer with dest %s" % idx)
-        else:
-            for peer in self.peers:
-                if peer.peer.id.value == idx:
-                    return peer
-            raise IndexError("No peer with dest hash %s" % idx)
-    elif isinstance(idx, KHash):
-        for peer in self.peers:
-            if peer.peer.id == idx:
-                return peer
-            raise IndexError("No peer with id %s" % idx)
-    else:
-        raise IndexError("Invalid selector %s" % repr(idx))
-
-</t>
-<t tx="aum.20040807004327.8">def __len__(self):
-    return len(self.peers)
-
-</t>
-<t tx="aum.20040807004327.9">def __getslice__(self, fromidx, toidx):
-    return KPeerQueryTable(self.peers[fromidx:toidx], self.sorthash)
-
-</t>
-<t tx="aum.20040807004327.10">def __iter__(self):
-    return iter(self.peers)
-
-</t>
-<t tx="aum.20040807004327.11">def sort(self):
-    """
-    Sort the table in order of increasing distance from self.sorthash
-    """
-    self.peers.sort()
-
-</t>
-<t tx="aum.20040807004327.12">def select(self, criterion):
-    """
-    Returns a table of items for which criterion(item) returns True
-    Otherwise, if 'criterion' is a string, returns the items whose
-    state == criterion.
-    Otherwise, if 'criterion' is a list or tuple, return the items
-    whose state is one of the elements in criterion
-    """
-    if callable(criterion):
-        func = criterion
-    elif type(criterion) in [type(()), type([])]:
-        func = lambda p: p.state in criterion
-    else:
-        func = lambda p: p.state == criterion
-
-    recs = []
-    for peerRec in self.peers:
-        if func(peerRec):
-            recs.append(peerRec)
-    return self.newtable(recs)
-
-</t>
-<t tx="aum.20040807004327.13">def filter(self, func):
-    """
-    Eliminate, in place, all items where func(item) returns False
-    """
-    for peerRec in self.peers:
-        if not func(peerRec):
-            self.peers.remove(peerRec)
-
-</t>
-<t tx="aum.20040807004327.14">def purge(self, func):
-    """
-    Eliminate, in place, all items where func(item) returns True
-    """
-    if 0 and desperatelyDebugging:
-        set_trace()
-    for peerRec in self.peers:
-        if func(peerRec):
-            self.peers.remove(peerRec)
-
-</t>
-<t tx="aum.20040807004327.15">def chooseN(self, n):
-    """
-    Randomly select n peer query records
-    """
-    candidates = self.peers[:]
-
-    self.log(3, "candidates = %s" % repr(candidates))
-
-    chosen = []
-    i = 0
-
-    if len(candidates) &lt;= n:
-        chosen = candidates
-    else:
-        while i &lt; n:
-            try:
-                peer = random.choice(candidates)
-            except:
-                self.log(2, "failed to choose one of %s" % repr(candidates))
-                raise
-            chosen.append(peer)
-            candidates.remove(peer)
-            i += 1
-
-    return self.newtable(chosen)
-
-</t>
-<t tx="aum.20040807004434">def __str__(self):
-    return "&lt;KPeerQueryTable: %d peers&gt;" % len(self) #.peers)
-
-def __repr__(self):
-    return str(self)
-
-</t>
-<t tx="aum.20040807013038"></t>
-<t tx="aum.20040807013038.1"></t>
-<t tx="aum.20040807013038.2"></t>
-<t tx="aum.20040807013038.3"></t>
-<t tx="aum.20040807013038.4"></t>
-<t tx="aum.20040807013038.5"></t>
-<t tx="aum.20040807013038.6"></t>
-<t tx="aum.20040807013038.7"></t>
-<t tx="aum.20040807013038.8"></t>
-<t tx="aum.20040807013038.9"></t>
-<t tx="aum.20040807013411">def returnValue(self, items):
-    """
-    override with a nicer call sig
-    """
-    # a hack for testing - save this RPC object into the node
-    # so we can introspect it
-    self.localNode.lastrpc = self
-
-    items = items[:maxBucketSize]
-
-    self.reportStats()
-
-    KRpc.returnValue(self, items, result=items)
-
-
-</t>
-<t tx="aum.20040807013835">def newtable(self, items, state=None, **kw):
-    """
-    Returns a new KPeerQueryTable object, based on this
-    one, but containing 'items'
-    """
-    tab = KPeerQueryTable(items, sorthash=self.sorthash, state=state, **kw)
-    return tab
-
-</t>
-<t tx="aum.20040807013944"></t>
-<t tx="aum.20040807014538">def __add__(self, other):
-    self.extend(other)
-
-</t>
-<t tx="aum.20040807033258">def __init__(self, peer, table, state=None, **kw):
-
-    self.peer = peer
-    self.dest = peer.dest
-    self.deadline = time.time() + timeout['findNode']
-    self.table = table
-
-    # state is always one of:
-    #  - 'start'        - have not yet sent query to peer
-    #  - 'recommended'  - peer was recommended by another peer, no query sent
-    #  - 'queried'      - sent query, awaiting reply or timeout
-    #  - 'replied'      - this peer has replied to our query
-    #  - 'timeout'      - timed out waiting for peer reply
-    #  - 'toofar'       - too far away to be of interest
-    #  - 'closest'      - this peer is one of the closest so far
-
-    if state == None:
-        state = 'start'
-    if not isinstance(state, str):
-        raise Exception("Invalid state %s" % state)
-
-    self.state = state
-
-    self.__dict__.update(kw)
-
-</t>
-<t tx="aum.20040807033258.1">def hasTimedOut(self, now=None):
-    if now == None:
-        now = time.time()
-    return self.state == 'queried' and now &gt; self.deadline
-
-</t>
-<t tx="aum.20040807033258.2">def __cmp__(self, other):
-
-    return cmp(self.peer.id.rawdistance(self.table.sorthash),
-               other.peer.id.rawdistance(self.table.sorthash))
-
-</t>
-<t tx="aum.20040807033818">def __lt__(self, other):
-    return (cmp(self, other) &lt; 0)
-
-def __le__(self, other):
-    return (cmp(self, other) &lt;= 0)
-
-def __gt__(self, other):
-    return (cmp(self, other) &gt; 0)
-
-def __ge__(self, other):
-    return (cmp(self, other) &lt;= 0)
-
-</t>
-<t tx="aum.20040807033818.1">def isCloserThanAllOf(self, tab):
-    """
-    returns True if this peerRec is closer to the desired hash
-    than all of the peerRecs in table 'tab'
-    """
-    if not isinstance(tab, KPeerQueryTable):
-        self.log(2, "invalid qtable %s" % repr(tab))
-        raise Exception("invalid qtable %s" % repr(tab))
-    
-    for rec in tab:
-        if self &gt; rec:
-            return False
-    return True
-
-</t>
-<t tx="aum.20040807034729">def isCloserThanOneOf(self, tab):
-    """
-    returns True if this peerRec is closer to the desired hash
-    than one or more of of the peerRecs in table 'tab'
-    """
-    if not isinstance(tab, KPeerQueryTable):
-        self.log(2, "invalid qtable %s" % repr(tab))
-        raise Exception("invalid qtable %s" % repr(tab))
-    
-    for rec in tab:
-        if self &lt; rec:
-            return True
-    return False
-
-</t>
-<t tx="aum.20040807044007">def __contains__(self, other):
-    self.log(5, "testing if %s is in %s" % (other, self.peers))
-    for peerRec in self.peers:
-        if peerRec.peer.dest == other.peer.dest:
-            return True
-    return False
-
-</t>
-<t tx="aum.20040807044007.1">def sendOneQuery(self, peerRec):
-    """
-    Sends off a query to a single peer
-    """
-    if peerRec.state != 'start':
-        self.log(2, "duh!! peer state %s:\n%s" % (peerRec.state, peerRec))
-        return 
-
-    msgId = self.localNode._msgIdAlloc()
-    self.bindPeerReply(peerRec.peer, msgId)
-    peerRec.msgId = msgId
-
-    if self.type == 'findData':
-        peerRec.peer.send_findData(hash=self.hashWanted, msgId=msgId)
-    else:
-        peerRec.peer.send_findNode(hash=self.hashWanted, msgId=msgId)
-
-    peerRec.state = 'queried'
-
-    self.numQueriesPending += 1
-
-    self.numQueriesSent += 1
-
-</t>
-<t tx="aum.20040807052750">def run(self, func=None):
-    """
-    Runs the core in foreground, with the client func in background
-    """
-    if func==None:
-        func = test
-
-    self.bg = False
-
-    thread.start_new_thread(self.runClient, (func,))
-    
-    set_trace()
-
-    self.threadRxPackets()
-
-</t>
-<t tx="aum.20040807053418">def stop(self):
-    self.isRunning = False
-
-</t>
-<t tx="aum.20040807114327">def runClient(self, func):
-
-    self.log(3, "Core: running client func")
-    try:
-        func()
-    except:
-        traceback.print_exc()
-    self.log(3, "Core: client func exited")
-    self.stop()
-</t>
-<t tx="aum.20040808133629">def putKey(self, key, val, keyIsHashed=False):
-    """
-    Stores a string into this storage under the key 'key'
-
-    Returns True if key was saved successfully, False if not
-    """
-    try:
-        if keyIsHashed:
-            keyHashed = key
-        else:
-            keyHashed = shahash(key)
-        keyHashed = keyHashed.lower()
-        keyPath = os.path.join(self.keysDir, keyHashed)
-        file(keyPath, "wb").write(val)
-        self.log(4, "stored key: '%s'\nunder hash '%s'\n(keyIsHashed=%s)" % (
-            key, keyHashed, keyIsHashed))
-        return True
-    except:
-        traceback.print_exc()
-        self.log(3, "failed to store key")
-        return False
-
-</t>
-<t tx="aum.20040808133629.1">def shahash(somestr, bin=False):
-    shaobj = sha.new(somestr)
-    if bin:
-        return shaobj.digest()
-    else:
-        return shaobj.hexdigest()
-
-</t>
-<t tx="aum.20040808133941">def getKey(self, key, keyIsHashed=False):
-    """
-    Attempts to retrieve item from node's local file storage, which was
-    stored with key 'key'.
-    
-    Returns value as a string if found, or None if not present
-    """
-    try:
-        if keyIsHashed:
-            keyHashed = key
-        else:
-            keyHashed = shahash(key)
-        
-        keyHashed = keyHashed.lower()
-        self.log(4, "key=%s, keyHashed=%s, keyIsHashed=%s" % (key, keyHashed, keyIsHashed))
-
-        keyPath = os.path.join(self.keysDir, keyHashed)
-    
-        if os.path.isfile(keyPath):
-            return file(keyPath, "rb").read()
-        else:
-            return None
-    except:
-        traceback.print_exc()
-        self.log(3, "error retrieving key '%s'" % key)
-        return None
-
-</t>
-<t tx="aum.20040808134739">def __init__(self, localNode, client=None, **kw):
-    """
-    Creates and launches a STORE rpc
-    
-    Arguments:
-        - localNode - the node performing this RPC
-        - client - see KRpc.__init__
-
-    Keywords:
-        - key - the key under which we wish to save the data
-        - value - the value we wish to save
-        - local - True/False:
-            - if True, only save in local store
-            - if False, do a findNode to find the nodes to save the
-              key to, and tell them to save it
-          default is True
-    """
-    self.key = kw['key']
-    #self.keyHashed = shahash(self.key)
-    self.keyHashed = self.key
-    self.value = kw['value']
-    self.isLocalOnly = kw.get('local', True)
-
-    # set 'splitting' flag to indicate if we need to insert as splitfiles
-    self.splitting = len(self.value) &gt; maxValueSize
-
-    self.log(4, "isLocalOnly=%s" % self.isLocalOnly)
-
-    if kw.has_key('cbArgs'):
-        KRpc.__init__(self, localNode, client, cbArgs=kw['cbArgs'])
-    else:
-        KRpc.__init__(self, localNode, client)
-
-</t>
-<t tx="aum.20040808134739.1">def start(self):
-    """
-    Kicks off this RPC
-    """
-    # if too big, then break up into &lt;30k chunks
-    if self.splitting:
-        self.storeSplit()
-        return
-
-    # not too big - prefix a 0 chunk count, and go ahead as a single entity
-    self.value = "chunks:0\n" + self.value
-        
-    # if local only, or no peers, just save locally
-    if self.isLocalOnly or len(self.localNode.peers) == 0:
-        result = self.localNode.storage.putKey(self.keyHashed, self.value, keyIsHashed=True)
-        if result:
-            result = 1
-        else:
-            result = 0
-        self.returnValue(result)
-        return
-
-    # no - se have to find peers to store the key to, and tell them to
-    # store the key
-    
-    # launch a findNode rpc, continue in our callback
-    KRpcFindNode(self.localNode, self.on_doneFindNode,
-                 hash=self.keyHashed, raw=True, local=False)
-    return
-
-
-</t>
-<t tx="aum.20040808135302">def on_doneFindNode(self, lst):
-    """
-    Receive a callback from findNode
-    
-    Send STORE command to each node that comes back
-    """
-    localNode = self.localNode
-
-    # normalise results
-    normalisePeer = localNode._normalisePeer
-    peers = [normalisePeer(p) for p in lst] # wrap in KPeer objects
-
-    self.log(2, "STORE RPC findNode - got peers %s" % repr(peers))
-
-    i = 0
-
-    self.numPeersSucceeded = 0
-    self.numPeersFailed = 0
-    self.numPeersFinished = 0
-
-    # and fire off store messages for each peer
-    for peer in peers:
-
-        if peer.dest == localNode.dest:
-            self.log(3, "storing to ourself")
-            localNode.storage.putKey(self.keyHashed, self.value, keyIsHashed=True)
-            self.numPeersSucceeded += 1
-            self.numPeersFinished += 1
-        else:
-            msgId = self.localNode._msgIdAlloc()
-            self.log(4, "forwarding store cmd to peer:\npeer=%s\nmsgId=%s" % (peer, msgId))
-            self.bindPeerReply(peer, msgId)
-            peer.send_store(key=self.keyHashed, value=self.value, msgId=msgId)
-        i += 1
-        if i &gt;= numStorePeers:
-            break
-
-    self.nextTickTime = time.time() + timeout['store']
-
-    self.log(2, "Sent store cmd to %s peers, awaiting responses" % i)
-
-    self.numPeersToStore = i
-
-
-</t>
-<t tx="aum.20040808140937">def returnValue(self, result):
-    """
-    an override with a nicer call sig
-    """
-    # a hack for testing - save this RPC object into the node
-    # so we can introspect it
-    self.localNode.lastrpc = self
-
-    try:
-        KRpc.returnValue(self, result, status=result)
-    except:
-        traceback.print_exc()
-        self.log(3, "Failed to return %s" % repr(result))
-        KRpc.returnValue(self, 0, status=0)
-
-</t>
-<t tx="aum.20040808140937.1">def on_reply(self, peer, msgId, **details):
-    """
-    callback which fires when we get a reply from a STORE we sent to a
-    peer
-    """
-    self.numPeersSucceeded += 1
-    self.numPeersFinished += 1
-    
-    if self.numPeersFinished == self.numPeersToStore:
-        # rpc is finished
-        self.returnValue(True)
-
-</t>
-<t tx="aum.20040808140937.2">def on_tick(self):
-
-    self.log(3, "Timeout awaiting store reply from %d out of %d peers" % (
-        self.numPeersToStore - self.numPeersSucceeded, self.numPeersToStore))
-
-    if self.numPeersSucceeded == 0:
-        self.log(3, "Store timeout - no peers replied, storing locally")
-        self.localNode.storage.putKey(self.keyHashed, self.value, keyIsHashed=True)
-
-    self.returnValue(True)
-
-</t>
-<t tx="aum.20040808142950">def _on_store(self, peer, msgId, **kw):
-    """
-    Handles incoming STORE command
-    """
-    self.log(4, "got STORE rpc from upstream:\npeer=%s\nmsgId=%s\nkw=%s" % (peer, msgId, kw))
-
-    KRpcStore(self, (peer, msgId), local=True, **kw)
-
-</t>
-<t tx="aum.20040808153427">def start(self):
-    """
-    Kicks off the RPC.
-    If requested key is stored locally, simply returns it.
-    Otherwise, falls back on parent method
-    """
-    # if we posses the data, just return the data
-    value = self.localNode.storage.getKey(self.hashWanted.asHex(), keyIsHashed=True)
-    if value != None:
-        self.log(4, "Found required value in local storage")
-        self.log(4, "VALUE='%s'" % value)
-        self.on_gotValue(value, self.hashWanted.asHex())
-        return
-
-    # no such luck - pass on to parent
-    KRpcFindNode.start(self)
-
-</t>
-<t tx="aum.20040808160207">def asHex(self):
-    return ("%040x" % self.value).lower()
-
-</t>
-<t tx="aum.20040808163651">def on_reply(self, peer, msgId, **details):
-    """
-    Callback for FIND_NODE reply
-    """
-    res = details.get('result', None)
-    if isinstance(res, str):
-        self.on_gotValue(res, self.hashWanted.asHex())
-    else:
-        KRpcFindNode.on_reply(self, peer, msgId, **details)
-
-</t>
-<t tx="aum.20040808203152">def __del__(self):
-    """
-    Cleanup
-    """
-
-</t>
-<t tx="aum.20040808213724">@first #! /usr/bin/env python
-
-"""
-A simple Hashcash implementation
-
-Visit U{http://www.hashcash.org} for more info about
-the theory and usage of hashcash.
-
-Run this module through epydoc to get pretty doco.
-
-Overview:
-    - implements a class L{HashCash}, with very configurable parameters
-    - offers two convenience wrapper functions, L{generate} and L{verify},
-      for those who can't be bothered instantiating a class
-    - given a string s, genToken produces a hashcash token
-      string t, as binary or base64
-    - generating t consumes a lot of cpu time
-    - verifying t against s is almost instantaneous
-    - this implementation produces clusters of tokens, to even out
-      the token generation time
-
-Performance:
-    - this implementation is vulnerable to:
-        - people with lots of computers, especially big ones
-        - people writing bruteforcers in C (python is way slow)
-    - even with the smoothing effect of creating token clusters,
-      the time taken to create a token can vary by a factor of 7
-
-Theory of this implementation:
-
-    - a hashcash token is created by a brute-force algorithm
-      of finding an n-bit partial hash collision
-
-    - given a string s, and a quality level q,
-      generate a 20-byte string h, such that:
-        
-        1. h != s
-        2. len(h) == 20
-        3. ((sha(s) xor sha(h)) and (2 ^ q - 1)) == 0
-    
-    - in other words, hash(h) and hash(s) have q least
-      significant bits in common
-
-If you come up with a faster, but PURE PYTHON implementation,
-using only modules included in standard python distribution,
-please let me know so I can upgrade mine or link to yours.
-
-Written by David McNab, August 2004
-Released to the public domain.
-"""
-@others
-
-</t>
-<t tx="aum.20040808221610">import sha, array, random, base64, math
-from random import randint
-
-shanew = sha.new
-
-</t>
-<t tx="aum.20040808221610.1">def generate(value, quality, b64=False):
-    """
-    Generates a hashcash token
-
-    This is a convenience wrapper function which saves you from having to
-    instantiate a HashCash object.
-    
-    Arguments:
-        - value - a string against which to generate token
-        - quality - an int from 1 to 160 - typically values are 16 to 30
-        - b64 - if True, return the token as base64 (suitable for email,
-          news, and other text-based contexts), otherwise return a binary string
-    
-    Quality values for desktop PC usage should typically be between 16 and 30.
-    Too low, and it makes an attacker's life easy.
-    Too high, and it makes life painful for the user.
-    """
-    if b64:
-        format = 'base64'
-    else:
-        format = 'binary'
-
-    h = HashCash(quality=quality, format=format)
-
-    return h.generate(value)
-
-</t>
-<t tx="aum.20040808221610.3">def binify(L):
-    """
-    Convert a python long int into a binary string
-    """
-    res = []
-    while L:
-        res.append(chr(L &amp; 0xFF))
-        L &gt;&gt;= 8
-    res.reverse()
-    return "".join(res)
-
-</t>
-<t tx="aum.20040808221610.4">def intify(s):
-    """
-    Convert a binary string to a python long int
-    """
-    n = 0L
-    for c in s:
-        n = (n &lt;&lt; 8) | ord(c)
-    return n
-
-</t>
-<t tx="aum.20040808221925"># your own config settings - set these to get a good trade-off between
-# token size and uniformity of time taken to generate tokens
-#
-# the final token size will be tokenSize * chunksPerToken for binary
-# tokens, or ceil(4/3 * tokenSize * chunksPerToken) for base64 tokens
-#
-# the reason for building a token out of multiple token chunks is to
-# try to even out the time taken for token generation
-# 
-# without this, token generation time is very random, with some tokens
-# generating almost instantaneously, and other tokens taking ages
-
-defaultChunkSize = 3        # size of each chunk in a token
-defaultNumChunks = 12       # number of chunks in each token
-defaultQuality = 12         # number of partial hash collision bits required
-defaultFormat = 'base64'    # by default, return tokens in base64 format
-defaultVerbosity = 0        # increase this to get more verbose output
-
-</t>
-<t tx="aum.20040808223205">def verify(value, quality, token):
-    """
-    Verifies a hashcash token.
-
-    This is a convenience wrapper function which saves you from having to
-    instantiate a HashCash object.
-
-    Arguments:
-        - value - the string against which to check the hashcash token
-        - quality - the number of bits of token quality we require
-        - token - a hashcash token string
-    """
-    h = HashCash(quality=quality)
-
-    return h.verify(value, token)
-
-</t>
-<t tx="aum.20040808224404">def test(nbits=14):
-    """
-    Basic test function - perform encoding and decoding,
-    in plain and base64 formats, using the wrapper functions
-    """
-    print "Test, using wrapper functions"
-
-    value = _randomString()
-    print "Generated random string\n%s" % value
-    print
-
-    print "Generating plain binary %s-bit token for:\n%s" % (nbits, value)
-    tok = generate(value, nbits)
-
-    print "Got token %s, now verifying" % repr(tok)
-    result = verify(value, nbits, tok)
-
-    print "Verify = %s" % repr(result)
-    print
-
-    print "Now generating base64 %s-bit token for:\n%s" % (nbits, value)
-    tok = generate(value, nbits, True)
-
-    print "Got base64 token %s, now verifying" % repr(tok)
-    result = verify(value, nbits, tok)
-
-    print "Verify = %s" % repr(result)
-
-</t>
-<t tx="aum.20040808231518"># get a boost of speed if psyco is available on target machine
-try:
-    import psyco
-    psyco.bind(genToken)
-    psyco.bind(binify)
-    psyco.bind(intify)
-except:
-    pass
-
-</t>
-<t tx="aum.20040809135303">class HashCash:
-    """
-    Class for creating/verifying hashcash tokens
-
-    Feel free to subclass this, overriding the default attributes:
-        - chunksize
-        - numchunks
-        - quality
-        - format
-        - verbosity
-    """
-    @others
-</t>
-<t tx="aum.20040809135303.1">def __init__(self, **kw):
-    """
-    Create a HashCash object
-    
-    Keywords:
-        - chunksize - size of each token chunk
-        - numchunks - number of chunks per token
-        - quality - strength of token, in bits:
-            - legal values are 1 to 160
-            - typical values are 10 to 30, larger values taking much
-              longer to generate
-        - format - 'base64' to output tokens in base64 format; any other
-          value causes tokens to be generated in binary string format
-        - verbosity - verbosity of output messages:
-            - 0 = silent
-            - 1 = critical only
-            - 2 = noisy
-    """
-    for key in ['chunksize', 'numchunks', 'quality', 'format', 'verbosity']:
-        if kw.has_key(key):
-            setattr(self, key, kw[key])
-
-    self.b64ChunkLen = int(math.ceil(self.chunksize * 4.0 / 3))
-
-</t>
-<t tx="aum.20040809135303.2">def generate(self, value):
-    """
-    Generate a hashcash token against string 'value'
-    """
-    quality = self.quality
-    mask = 2 ** quality - 1
-    hV = sha.new(value).digest()
-    nHV = intify(hV)
-    
-    maxTokInt = 2 ** (self.chunksize * 8)
-
-    tokenChunks = []
-    chunksPerToken = self.numchunks
-
-    # loop around generating random strings until we get one which,
-    # when xor'ed with value, produces a hash with the first n bits
-    # set to zero
-    while 1:
-        nTok = randint(0, maxTokInt)
-        sNTok = binify(nTok)
-        hSNTok = shanew(sNTok).digest()
-        nHSNTok = intify(hSNTok)
-        if (nHV ^ nHSNTok) &amp; mask == 0:
-            # got a good token
-            if self.format == 'base64':
-                if not self._checkBase64(sNTok):
-                    # chunk fails to encode/decode base64
-                    if self.verbosity &gt;= 2:
-                        print "Ditching bad candidate token"
-                    continue
-                bSNTok = self._enc64(sNTok)
-                if self.verbosity &gt;= 2:
-                    print "encoded %s to %s, expect chunklen %s" % (
-                        repr(sNTok), repr(bSNTok), self.b64ChunkLen)
-                sNTok = bSNTok
-            # got something that works, add it to chunks, return if we got enough chunks
-            if sNTok in tokenChunks:
-                continue # already got this one
-            tokenChunks.append(sNTok)
-            if len(tokenChunks) == chunksPerToken:
-                return "".join(tokenChunks)
-
-</t>
-<t tx="aum.20040809135303.3">def verify(self, value, token):
-    """
-    Verifies a hashcash token against string 'value'
-    """
-    if self.verbosity &gt;= 2:
-        print "Verify: checking token %s (len %s) against %s" % (token, len(token), value)
-    # mask is an int with least-significant 'q' bits set to 1
-    mask = 2 ** self.quality - 1
-
-    # breaking up token into its constituent chunks
-    chunks = []
-
-    # verify token size
-    if len(token) != self.chunksize * self.numchunks:
-        # try base64
-        decoded = False
-        try:
-            for i in range(0, self.numchunks):
-                b64chunk = token[(i * self.b64ChunkLen) : ((i + 1) * self.b64ChunkLen)]
-                chunk = self._dec64(b64chunk)
-                if len(chunk) != self.chunksize:
-                    if self.verbosity &gt;= 2:
-                        print "Bad chunk length in decoded base64, wanted %s, got %s" % (
-                            self.chunksize, len(chunk))
-                    return False
-                chunks.append(chunk)
-        except:
-            if self.verbosity &gt;= 2:
-                if decoded:
-                    print "Bad token length"
-                else:
-                    print "Base64 decode failed"
-            return False
-    else:
-        # break up token into its chunks
-        for i in range(0, self.numchunks):
-            chunks.append(token[(i * self.chunksize) : ((i + 1) * self.chunksize)])
-
-    # produce hash string and hash int for input string
-    hV = sha.new(value).digest()
-    nHv = intify(hV)
-
-    # test each chunk
-    if self.verbosity &gt;= 2:
-        print "chunks = %s" % repr(chunks)
-
-    while chunks:
-        chunk = chunks.pop()
-
-        # defeat duplicate chunks
-        if chunk in chunks:
-            if self.verbosity &gt;= 2:
-                print "Rejecting token chunk - duplicate exists"
-            return False
-
-        # hash the string and the token
-        hTok = sha.new(chunk).digest()
-    
-        # defeat the obvious attack
-        if hTok == hV:
-            if self.verbosity &gt;= 2:
-                print "Rejecting token chunk - equal to token"
-            return False
-    
-        # test if these hashes have the least significant n bits in common
-        nHTok = intify(hTok)
-        if (nHTok ^ nHv) &amp; mask != 0:
-            # chunk failed
-            if self.verbosity &gt;= 2:
-                print "Rejecting token chunk %s - hash test failed" % repr(chunk)
-            return False
-    
-    # pass
-    return True
-
-</t>
-<t tx="aum.20040809135512">def ctest(quality=14):
-    """
-    Basic test function - perform token generation and verify, against
-    a random string. Instantiate a HashCash class instead of just using the
-    wrapper funcs.
-    """
-    print "Test using HashCash class"
-
-    value = _randomString()
-    print "Generated random string\n%s" % value
-    print
-
-    hc = HashCash(quality=quality, format='base64')
-
-    print "Generating plain binary %s-bit token for:\n%s" % (quality, value)
-    tok = hc.generate(value)
-
-    print "Got token %s, now verifying" % repr(tok)
-    result = hc.verify(value, tok)
-
-    print "Verify = %s" % repr(result)
-    print
-
-</t>
-<t tx="aum.20040809135512.1">if __name__ == '__main__':
-
-    test()
-
-</t>
-<t tx="aum.20040809145111">def ntest():
-    """
-    This function does 256 key generations in a row, and dumps
-    some statistical results
-    """
-    # adjust these as desired
-    chunksize=3
-    numchunks=32
-    quality=6
-    numIterations = 256
-
-    import time
-    try:
-        import stats
-    except:
-        print "This test requires the stats module"
-        print "Get it (and its dependencies) from:"
-        print "http://www.nmr.mgh.harvard.edu/Neural_Systems_Group/gary/python.html"
-        return
-
-    print "Thrash test"
-
-    times = []
-
-    # create a hashcash token generator object
-    hc = HashCash(
-        chunksize=chunksize,
-        numchunks=numchunks,
-        quality=quality
-        )
-
-    # 256 times, create a random string and a matching hashcash token
-    for i in range(numIterations):
-
-        value = _randomString()
-
-        # measure time for a single token generation
-        then = time.time()    
-        tok = hc.generate(value)
-        now = time.time()
-        times.append(now - then)
-
-        # sanity check, make sure it's valid
-        result = hc.verify(value, tok)
-        if not result:
-            print "Verify failed, token length=%s" % len(tok)
-            return
-
-        print "Generated %s of %s tokens" % (i, numIterations)
-
-    print "---------------------------------"
-    print "Thrash test performance results"
-    print "Token quality: %s bits" % quality
-    print "Min=%.3f max=%.3f max/min=%.3f mean=%.3f, median=%.3f, stdev=%.3f" % (
-        min(times),
-        max(times),
-        max(times)/min(times),
-        stats.lmean(times),
-        stats.lmedian(times),
-        stats.lstdev(times)
-        )
-
-</t>
-<t tx="aum.20040809145905">def _checkBase64(self, item):
-    """
-    Ensures the item correctly encodes then decodes to/from base64
-    """
-    #if self.verbose:
-    #    print "Checking candidate token"
-    enc = self._enc64(item)
-    if len(enc) != self.b64ChunkLen:
-        if self.verbosity &gt;= 1:
-            print "Bad candidate token"
-        return False
-    return self._dec64(enc) == item
-
-</t>
-<t tx="aum.20040809150949">def _enc64(self, item):
-    """
-    Base64-encode a string, remove padding
-    """
-    enc = base64.encodestring(item).strip()
-    while enc[-1] == '=':
-        enc = enc[:-1]
-    return enc
-
-</t>
-<t tx="aum.20040809150949.1">def _dec64(self, item):
-    """
-    Base64-decode a string
-    """
-    dec = base64.decodestring(item+"====")
-    return dec
-
-</t>
-<t tx="aum.20040809160231">def _randomString():
-    """
-    For our tests below.
-    Generates a random-length human-readable random string,
-    between 16 and 80 chars
-    """
-    chars = []
-    slen = randint(16, 80)
-    for i in range(slen):
-        chars.append(chr(randint(32, 128)))
-    value = "".join(chars)
-    return value
-
-</t>
-<t tx="aum.20040809162443"># override these at your pleasure
-
-chunksize = defaultChunkSize
-numchunks = defaultNumChunks
-quality = defaultQuality
-format = defaultFormat
-verbosity = defaultVerbosity
-
-</t>
-<t tx="aum.20040809222157">def dump(self, detailed=0):
-    """
-    Outputs a list of nodes and their connections
-    """
-    if detailed:
-        self.dumplong()
-        return
-
-    for node in self.nodes:
-        print node.name + ":"
-        print "  " + ", ".join([self.getPeerName(peer) for peer in node.peers])
-
-</t>
-<t tx="aum.20040810115020">def getPeerName(self, peer):
-    for n in self.nodes:
-        if n.dest == peer.dest:
-            return n.name
-    return "&lt;??%s&gt;" % n.dest[:8]
-
-</t>
-<t tx="aum.20040810122748">def whohas(self, key):
-    print "Nodes having key %s:" % key
-
-    h = KHash(key)
-
-    def hcmp(n1, n2):
-        res = cmp(h.rawdistance(n1.id), h.rawdistance(n2.id))
-        #print "compared: %s %s = %s" % (n1.idx, n2.idx, res)
-        return res
-
-    i = 0
-
-    nodes = self.nodes[:]
-    nodes.sort(hcmp)
-
-    for node in nodes:
-        if node.storage.getKey(key) != None:
-            i += 1
-            print "%3s" % node.idx,
-            if i % 16 == 0:
-                print
-
-</t>
-<t tx="aum.20040810125759">def whocanfind(self, key):
-    """
-    Produces a list of nodes which can find key 'key'
-    """
-    successes = []
-    failures = []
-    print "Nodes which can find key %s" % key
-    for i in range(len(self.nodes)):
-        node = self.nodes[i]
-        if node.get(key) != None:
-            print "  %s found it" % node.name
-            successes.append(i)
-        else:
-            print "  %s failed" % node.name
-            failures.append(i)
-
-    print "Successful finds: %s" % repr(successes)
-    print "Failed finds: %s" % repr(failures)
-
-</t>
-<t tx="aum.20040810131309">def closestto(self, key):
-    """
-    Outputs a list of node names, in order of increasing 'distance'
-    from key
-    """
-    key = KHash(key)
-    def nodecmp(node1, node2):
-        #self.log(3, "comparing node %s with %s" % (node1, node2))
-        return cmp(node1.id.rawdistance(key), node2.id.rawdistance(key))
-    
-    nodes = self.nodes[:]
-    nodes.sort(nodecmp)
-    print "Nodes, in order of increasing distance from '%s'" % key
-
-    i = 0
-    for node in nodes:
-        i += 1
-        print "%3s" % node.idx,
-        if i % 16 == 0:
-            print
-
-</t>
-<t tx="aum.20040810132855">def findnode(self, idx, key):
-    """
-    does a findnode on peer 'idx' against key 'key'
-    """
-    peers = self.nodes[idx]._findnode(key)
-    print "Findnode on node %s (%s) returned:" % (self.nodes[idx].name, key)
-
-    peers = [self.getPeerIdx(p) for p in peers]
-
-    i = 0
-    for p in peers:
-        print "%3d" % p,
-        i += 1
-        if i % 16 == 0:
-            print
-
-</t>
-<t tx="aum.20040810141626">def dumpids(self):
-    print "Nodes listed by name and id"
-    for i in range(len(self.nodes)):
-        node = self.nodes[i]
-        print "%s: %s (%s...)" % (i, node.name, node.id.asHex()[:10])
-</t>
-<t tx="aum.20040810142448">def dumplong(self):
-    """
-    Outputs a list of nodes and their connections
-    """
-    for node in self.nodes:
-        print "%s:   id=%s dest=%s" % (node.name, node.id.asHex()[:8], node.dest[:8])
-        for peer in node.peers:
-            npeer = self.getPeer(peer)
-            print "  %s: id=%s dest=%s" % (npeer.name, npeer.id.asHex()[:8], npeer.dest[:8])
-</t>
-<t tx="aum.20040810142611">def getPeer(self, peer):
-    for n in self.nodes:
-        if n.dest == peer.dest:
-            return n
-    return None
-
-</t>
-<t tx="aum.20040810224601">def logexc(verbosity, msg, nPrev=0, clsname=None):
-
-    fd = StringIO("%s\n" % msg)
-    traceback.print_exc(file=fd)
-    log(verbosity, fd.getvalue(), nPrev, clsname)
-
-</t>
-<t tx="aum.20040811013733">class KTestSocket(stasher.KBase):
-    """
-    Emulates an I2P Socket for testing
-    """
-    # class-global mapping of b64 dests to sockets
-    opensocks = {}
-    totalQueuedItems = 0
-    
-    def __init__(self, sessname, *args, **kw):
-
-        self.log(4, "creating simulated i2p socket %s" % sessname)
-        
-        # that'll do for pseudo-random dests
-        self.dest = stasher.shahash(sessname) + "0" * 256
-
-        # set up our inbound queue
-        self.queue = Queue.Queue()
-        
-        # register ourself
-        self.opensocks[self.dest] = self
-    
-    def __del__(self):
-
-        # deregister ourself
-        del self.opensocks[self.dest]
-
-    def sendto(self, data, flags, dest):
-        
-        self.opensocks[dest].queue.put((data, self.dest))
-        KTestSocket.totalQueuedItems += 1
-    
-    def recvfrom(self, *args):
-        
-        KTestSocket.totalQueuedItems -= 1
-        return self.queue.get()
-
-    def select(inlist, outlist, errlist, timeout=0):
-
-        log = stasher.log
-        log(5, "fake select called")
-        deadline = time.time() + timeout
-        while (time.time() &lt; deadline) and KTestSocket.totalQueuedItems == 0:
-            time.sleep(0.1)
-        if KTestSocket.totalQueuedItems == 0:
-            return [], [], []
-        socksWithData = []
-        for sock in inlist:
-            if not sock.queue.empty():
-                socksWithData.append(sock)
-        log(5, "fake select returning %s" % repr(socksWithData))
-        return socksWithData, [], []
-        
-    select = staticmethod(select)
-        
-    def setblocking(self, val):
-        
-        self.blocking = val
-    </t>
-<t tx="aum.20040811111244"></t>
-<t tx="aum.20040811111244.1">class KBase:
-    """
-    A mixin which adds a class-specific logger
-    """
-    def log(self, verbosity, msg):
-        
-        log(verbosity, msg, 1, self.__class__.__name__)
-
-    def logexc(self, verbosity, msg):
-
-        logexc(verbosity, msg, 1, self.__class__.__name__)
-
-</t>
-<t tx="aum.20040811160223">def debug():
-    """
-    Alternative testing entry point which runs the test() function
-    in background, and the engine in foreground, allowing the engine
-    to be stepped through with a debugger
-    """
-    global desperatelyDebugging
-    desperatelyDebugging = True
-    core.run()
-
-</t>
-<t tx="aum.20040811162256"></t>
-<t tx="aum.20040811162302">def on_reply(self, peer, msgId, **details):
-    """
-    Callback for FIND_NODE reply
-    """
-    # shorthand
-    peerRecs = self.peerRecs
-
-    # who replied?
-    try:
-        peerRec = peerRecs[peer]
-    except:
-        traceback.print_exc()
-        self.log(3, "discarding findNode reply from unknown peer %s %s, discarding" % (
-            peer, details))
-        return
-
-    if logVerbosity &gt;= 3:
-        try:
-            dests = "\n".join([d[:6] for d in details['nodes']])
-        except:
-            logexc(4, "*** find-node rpc reply = %s" % details)
-
-    self.log(3, "got findNode reply from %s:\n%s" % (peer, details))
-    self.unbindPeerReply(peer, msgId)
-
-    # save ref to this peer, it's seemingly good
-    self.localNode.addref(peerRec.peer)
-
-    # mark it as having replied
-    peerRec.state = 'replied'
-
-    # one less query to wait for
-    self.numQueriesPending -= 1
-
-    # save these as 'fromReply' peers
-    peersReturned = details.get('nodes', [])
-    peersReturned = [self.localNode._normalisePeer(p) for p in peersReturned]
-    peerRecsReturned = peerRecs.newtable(peersReturned, 'fromReply')
-    peerRecsReturned.sort()
-    peerRecsReturned.purge(lambda p:p in peerRecs or p.peer.dest == self.localNode.dest)
-
-    # update our node's KBucket
-    for peerObj in peersReturned:
-        dist = self.localNode.id.distance(peerObj.id)
-        self.localNode.buckets[dist].justSeenPeer(peerObj)
-
-    self.log(5, "peerRecsReturned = %s" % repr(peerRecsReturned))
-
-    if len(peerRecsReturned) &gt; 0:
-        peerRecs.extend(peerRecsReturned, 'fromReply')
-
-    if desperatelyDebugging:
-        print "TRACING???"
-        set_trace()
-
-    # are there any peers we're still waiting to hear from?
-    if self.numQueriesPending == 0 and len(peerRecs.select(('idle', 'fromQuery'))) == 0:
-
-        # query round is finished - see how good the results are
-        repliedPeers = peerRecs.select('replied')
-        self.log(3, "====== query round finished, got %s" % repr(repliedPeers))
-
-        # if this round returned any peers better than the ones we've already
-        # heard from, then launch another round of queries
-        candidates = peerRecs.select('candidate')
-        ncandidates = len(candidates)
-
-        # test all returned peers, and see if one or more is nearer than
-        # our candidates
-        closerPeers = []
-        gotNearer = 0
-        for rec in peerRecs.select('fromReply'):
-            if ncandidates == 0 or rec.isCloserThanOneOf(candidates):
-                self.log(3, "Got a closer peer (or no candiates yet)")
-                gotNearer = 1
-
-        if gotNearer:
-            # mark replied peers as candidates
-            for rec in peerRecs.select('replied'):
-                rec.state = 'candidate'
-            pass
-        else:
-            # no - all queries are exhausted - it's the end of this round
-            self.log(3, "Query round returned no closer peers")
-            self.returnTheBestWeGot()
-
-    self.sendSomeQueries()
-
-</t>
-<t tx="aum.20040811163127">def count(self, *args):
-    """
-    returns the number of records whose state is one of args
-    """
-    count = 0
-    for rec in self.peers:
-        if rec.state in args:
-            count += 1
-    return count
-
-</t>
-<t tx="aum.20040811221318">def changeState(self, oldstate, newstate):
-    """
-    for all recs of state 'oldstate', change their
-    state to 'newstate'
-    """
-    for p in self.peers:
-        if p.state == oldstate:
-            p.state = newstate
-</t>
-<t tx="aum.20040811221628">def checkEndOfRound(self):
-    """
-    Checks if we've hit the end of a query round.
-    If so, and if either:
-        - we've got some closer peers, OR
-        - we've heard from less than maxBucketSize peers,
-    fire off more queries
-    
-    Otherwise, return the best available
-    """
-    peerTab = self.peerTab
-
-    if core.fg:
-        set_trace()
-
-    # has this query round ended?
-    if peerTab.count('start', 'queried') &gt; 0:
-        # not yet
-        return
-
-    self.log(2, "********** query round ended")
-
-    # ------------------------------------
-    # end of round processing
-
-    self.numRounds += 1
-
-    # did we get any closer to required hash?
-    if self.type == 'findData' \
-            or self.gotAnyCloser() \
-            or peerTab.count('closest') &lt; maxBucketSize:
-
-        # yes - save these query results
-        self.log(4, "starting another round")
-        peerTab.changeState('replied', 'closest')
-        peerTab.changeState('recommended', 'start')
-
-        # cull the shortlist
-        self.log(2, "culling to k peers")
-        if peerTab.count('closest') &gt; maxBucketSize:
-            peerTab.sort()
-            excess = peerTab.select('closest')[maxBucketSize:]
-            excess.changeState('closest', 'toofar')
-            pass
-
-        # and start up another round
-        self.sendSomeQueries()
-
-    # did anything launch?
-    if peerTab.count('start', 'queried') == 0:
-        # no - we're screwed
-        self.returnTheBestWeGot()
-        
-    # done for now
-    return
-
-</t>
-<t tx="aum.20040811222934">def gotAnyCloser(self):
-    """
-    Tests if any peer records in state 'recommended' or 'replied'
-    are nearer than the records in state 'closest'
-    """
-    peerTab = self.peerTab
-
-    # get current closest peers
-    closest = peerTab.select('closest')
-
-    # if none yet, then this was just end of first round
-    if len(closest) == 0:
-        return True
-
-    # get the peers we're considering
-    #candidates = peerTab.select(('recommended', 'replied'))
-    candidates = peerTab.select('recommended')
-
-    # now test them
-    gotOneCloser = False
-    for c in candidates:
-        #if c.isCloserThanOneOf(closest):
-        if c.isCloserThanAllOf(closest):
-            return True
-
-    # none were closer
-    return False
-
-</t>
-<t tx="aum.20040811223457">def doput():
-    
-    n[0].put('roses', 'red')
-
-</t>
-<t tx="aum.20040811235333">def dump(self):
-
-    c = self.count
-    self.log(2,
-            "PeerQueryTable stats:\n"
-            "start:       %s\n"
-            "recommended: %s\n"
-            "queried:     %s\n"
-            "replied:     %s\n"
-            "timeout:     %s\n"
-            "closest:     %s\n"
-            "toofar:      %s\n"
-            "TOTAL:       %s\n" % (
-                c('start'),
-                c('recommended'),
-                c('queried'),
-                c('replied'),
-                c('timeout'),
-                c('closest'),
-                c('toofar'),
-                len(self.peers)))
-                
-    #states = [p.state for p in self.peers]
-    #self.log(3, "PeerQueryTable states:\n%s" % states)
-
-</t>
-<t tx="aum.20040812013918">class KTestMap(stasher.KBase):
-    """
-    Creates a random set of interconnections between nodes
-    in a test network
-    """
-    path = "testnet.topology-%s"
-
-    def __init__(self, numnodes):
-
-        path = self.path % numnodes
-        if os.path.isfile(path):
-            self.load(numnodes)
-            return
-
-        self.log(2, "Creating new test topology of %s nodes" % numnodes)
-
-        self.refs = {} # key is nodenum, val is list of ref nodenums
-        self.numnodes = numnodes
-        i = 0
-        for i in range(1, numnodes):
-            
-            # get a random number not equal to i
-            while 1:
-                ref = random.randrange(0, i)
-                if ref != i:
-                    break
-                
-            # add it
-            self.refs[i] = ref
-
-        # now point node 0 somewhere
-        self.refs[0] = random.randrange(1, i)
-
-        # and save it
-        self.save()
-
-    def __getitem__(self, idx):
-        """
-        Returns the ref num for node index idx
-        """
-        return self.refs[idx]
-    
-    def dump(self):
-        nodes = self.refs.keys()
-        nodes.sort()
-        for n in nodes:
-            print ("%2s -&gt; %2s" % (n, self.refs[n])),
-            if (n + 1) % 8 == 0:
-                print
-            else:
-                print "|",
-
-    def load(self, numnodes):
-        path = self.path % numnodes
-        encoded = file(path, "rb").read()
-        decoded = pickle.loads(encoded)
-        self.numnodes, self.refs = decoded
-        self.log(2, "Restored existing topology of %s nodes" % numnodes)
-
-    def save(self):
-        path = self.path % self.numnodes
-        encoded = pickle.dumps((self.numnodes, self.refs))
-        file(path, "wb").write(encoded)
-
-</t>
-<t tx="aum.20040812015208">def connect(self, topology=None):
-    """
-    Connect these nodes together 
-    """
-    if topology:
-        self.map = topology
-    else:
-        self.map = KTestMap(len(self.nodes))
-
-    nodeIdxs = self.map.refs.keys()
-    nodeIdxs.sort()
-    
-    for idx in nodeIdxs:
-        #print "Node %s, adding ref to node %s" % (idx, self.map[idx])
-        self[idx].addref(self[self.map[idx]])
-
-</t>
-<t tx="aum.20040812020746">def purge(self):
-
-    os.system("rm -rf ~/.i2pkademlia")
-
-</t>
-<t tx="aum.20040812110124">if __name__ == '__main__':
-
-    main()
-
-</t>
-<t tx="aum.20040812125235">def __del__(self):
-    
-    pass
-    #KTestNetwork.aNetworkExists = False
-
-</t>
-<t tx="aum.20040812152603">def cycle(self):
-
-    self.fg = True
-    self.threadRxPackets()
-
-</t>
-<t tx="aum.20040812221935">def findpath(self, i0, i1):
-    """
-    Tries to find a path from idx0 to idx1, printing out
-    the nodes along the way
-    """
-    def _findpath(self, idx0, idx1, tried):
-        #print "seeking path from %s to %s" % (idx0, idx1)
-        n0 = self.nodes[idx0]
-        n1 = self.nodes[idx1]
-        
-        n0peers = [self.getPeer(p) for p in n0.peers]
-    
-        n0peerIdxs = [self.nodes.index(p) for p in n0peers]
-        
-        possibles = []
-        for idx in n0peerIdxs:
-            if idx == idx1:
-                # success
-                return [idx1]
-            if idx not in tried:
-                possibles.append(idx)
-        
-        if possibles == []:
-            return None
-        
-        #print "  possibles = %s" % repr(possibles)
-    
-        for idx in possibles:
-            tried.append(idx)
-            res = _findpath(self, idx, idx1, tried)
-            if isinstance(res, list):
-                res.insert(0, idx)
-                return res
-    
-        return None
-
-    res = _findpath(self, i0, i1, [i0])
-    
-    if isinstance(res, list):
-        res.insert(0, i0)
-    
-    return res
-</t>
-<t tx="aum.20040812230933">def testconnectivity(self):
-    """
-    Ensures that every peer can reach every other peer
-    """
-    nNodes = len(self.nodes)
-    
-    for i in range(nNodes):
-        for j in range(nNodes):
-            if i != j and not self.findpath(i, j):
-                print "No route from node %s to node %s" % (i, j)
-                return False
-    print "Every node can reach every other node"
-    return True
-
-</t>
-<t tx="aum.20040813013718">def getPeerIdx(self, peer):
-
-    for i in range(len(self.nodes)):
-        n = self.nodes[i]
-        if n.dest == peer.dest:
-            return i
-    return None
-
-</t>
-<t tx="aum.20040813015858">def rawdistance(self, other):
-    """
-    calculates the 'distance' between this hash and another hash,
-    and returns it raw as this xor other
-    """
-    return self.value ^ other.value
-
-</t>
-<t tx="aum.20040813200853">def reportStats(self):
-    """
-    Logs a stat dump of query outcome
-    """
-    if self.isLocalOnly:
-        return
-    self.log(2,
-        "query terminated after %s rounds, %s queries, %s replies, %s recommendations" % (
-           (self.numRounds+1),
-           self.numQueriesSent,
-           (self.numReplies+1),
-           self.numPeersRecommended
-           )
-        )
-</t>
-<t tx="aum.20040813202242">@first #! /usr/bin/env python
-
-"""
-Implements a simulated kademlia test network
-"""
-
-@others
-
-</t>
-<t tx="aum.20040813202517"></t>
-<t tx="aum.20040813202541">@others
-</t>
-<t tx="aum.20040813202541.1">if __name__ == '__main__':
-
-    print "starting test"
-    pass
-    test()
-
-</t>
-<t tx="aum.20040813203116">import sys, os, Queue, pickle, time
-from pdb import set_trace
-
-import stasher
-
-
-</t>
-<t tx="aum.20040813203339">class KCore(stasher.KCore):
-    """
-    Override kademlia core to use simulated I2P sockets
-    """
-    def select(self, inlist, outlist, errlist, timeout):
-
-        #print "dummy select"
-        return KTestSocket.select(inlist, outlist, errlist, timeout)
-
-</t>
-<t tx="aum.20040813203339.1">class KNode(stasher.KNode):
-    """
-    Override kademlia node class to use simulated test socket
-    """
-    SocketFactory = KTestSocket
-
-</t>
-<t tx="aum.20040813203621"># number of nodes to build in test network
-numTestNodes = 100
-
-stasher.logToSocket = 19199
-</t>
-<t tx="aum.20040813204308"></t>
-<t tx="aum.20040813204308.1">SocketFactory = None # defaults to I2P socket
-
-</t>
-<t tx="aum.20040813204858">def select(self, inlist, outlist, errlist, timeout):
-    
-    return i2p.select.select(inlist, outlist, errlist, timeout)
-
-</t>
-<t tx="aum.20040813211551">def main():
-    """
-    Command line interface
-    """
-    global samAddr, clientAddr, logVerbosity, dataDir
-
-    argv = sys.argv
-    argc = len(argv)
-
-    try:
-        opts, args = getopt.getopt(sys.argv[1:],
-                                   "h?vV:S:C:sd:fl",
-                                   ['help', 'version', 'samaddr=', 'clientaddr=',
-                                    'verbosity=', 'status', 'datadir=', 'foreground',
-                                    'shortversion', 'localonly',
-                                    ])
-    except:
-        traceback.print_exc(file=sys.stdout)
-        usage("You entered an invalid option")
-
-    daemonise = True
-    verbosity = 2
-    debug = False
-    foreground = False
-    localOnly = False
-
-    for opt, val in opts:
-        
-        if opt in ['-h', '-?', '--help']:
-            usage(True)
-
-        elif opt in ['-v', '--version']:
-            print "Stasher version %s" % version
-            sys.exit(0)
-
-        elif opt in ['-V', '--verbosity']:
-            logVerbosity = int(val)
-
-        elif opt in ['-f', '--foreground']:
-            foreground = True
-        
-        elif opt in ['-S', '--samaddr']:
-            samAddr = val
-
-        elif opt in ['-C', '--clientaddr']:
-            clientAddr = val
-        
-        elif opt in ['-s', '--status']:
-            dumpStatus()
-
-        elif opt in ['-d', '--datadir']:
-            dataDir = val
-
-        elif opt == '--shortversion':
-            sys.stdout.write("%s" % version)
-            sys.stdout.flush()
-            sys.exit(0)
-
-        elif opt in ['-l', '--localonly']:
-            localOnly = True
-
-    #print "Debug - bailing"
-    #print repr(opts)
-    #print repr(args)
-    #sys.exit(0)
-
-    # Barf if no command given
-    if len(args) == 0:
-        err("No command given")
-        usage(0, 1)
-
-    cmd = args.pop(0)
-    argc = len(args)
-
-    #print "cmd=%s, args=%s" % (repr(cmd), repr(args))
-    
-    if cmd not in ['help', '_start', 'start', 'stop',
-                   'hello', 'get', 'put', 'addref', 'getref',
-                   'pingall']:
-        err("Illegal command '%s'" % cmd)
-        usage(0, 1)
-
-    if cmd == 'help':
-        usage()
-
-    # dirty hack
-    if foreground and cmd == 'start':
-        cmd = '_start'
-
-    # magic undocumented command name - starts node, launches its client server,
-    # this should only happen if we're spawned from a 'start' command
-    if cmd == '_start':
-        if argc not in [0, 1]:
-            err("start: bad argument count")
-            usage()
-        if argc == 0:
-            nodeName = defaultNodename
-        else:
-            nodeName = args[0]
-        
-        # create and serve a node
-        #set_trace()
-        node = KNode(nodeName)
-        node.start()
-        log(3, "Node %s launched, dest = %s" % (node.name, node.dest))
-        node.serve()
-        sys.exit(0)
-    
-    if cmd == 'start':
-        if argc not in [0, 1]:
-            err("start: bad argument count")
-            usage()
-        if argc == 0:
-            nodeName = defaultNodename
-        else:
-            nodeName = args[0]
-        pidFile = nodePidfile(nodeName)
-
-        if os.path.exists(pidFile):
-            err(("Stasher node '%s' seems to be already running. If you are\n" % nodeName)
-                +"absolutely sure it's not running, please remove its pidfile:\n"
-                +pidFile+"\n")
-            sys.exit(1)
-
-        # spawn off a node
-        import stasher
-        pid = spawnproc(sys.argv[0], "-S", samAddr, "-C", clientAddr, "_start", nodeName)
-        file(pidFile, "wb").write("%s" % pid)
-        print "Launched stasher node as pid %s" % pid
-        print "Pidfile is %s" % pidFile
-        sys.exit(0)
-
-    if cmd == 'stop':
-        if argc not in [0, 1]:
-            err("stop: bad argument count")
-            usage()
-        if argc == 0:
-            nodeName = defaultNodename
-        else:
-            nodename = args[0]
-
-        pidFile = nodePidfile(nodeName)
-
-        if not os.path.isfile(pidFile):
-            err("Stasher node '%s' is not running - cannot kill\n" % nodeName)
-            sys.exit(1)
-
-        pid = int(file(pidFile, "rb").read())
-        try:
-            killproc(pid)
-            print "Killed stasher node (pid %s)" % pid
-        except:
-            print "Failed to kill node (pid %s)" % pid
-        os.unlink(pidFile)
-        sys.exit(0)
-
-    try:
-        client = KNodeClient()
-    except:
-        traceback.print_exc()
-        err("Node doesn't seem to be up, or reachable on %s" % clientAddr)
-        return
-
-
-    if cmd == 'hello':
-        err("Node seems fine")
-        sys.exit(0)
-    
-    elif cmd == 'get':
-        if argc not in [1, 2]:
-            err("get: bad argument count")
-            usage()
-
-        key = args[0]
-
-        if argc == 2:
-            # try to open output file
-            path = args[1]
-            try:
-                outfile = file(path, "wb")
-            except:
-                err("Cannot open output file %s" % repr(path))
-                usage(0, 1)
-        else:
-            outfile = sys.stdout
-
-        if logVerbosity &gt;= 3:
-            sys.stderr.write("Searching for key - may take up to %s seconds or more\n" % (
-                timeout['findData']))
-        res = client.get(key, local=localOnly)
-        if res == None:
-            err("Failed to retrieve '%s'" % key)
-            sys.exit(1)
-        else:
-            outfile.write(res)
-            outfile.flush()
-            outfile.close()
-            sys.exit(0)
-
-    elif cmd == 'put':
-        if argc not in [1, 2]:
-            err("put: bad argument count")
-            usage()
-
-        key = args[0]
-
-        if argc == 2:
-            # try to open input file
-            path = args[1]
-            try:
-                infile = file(path, "rb")
-            except:
-                err("Cannot open input file %s" % repr(path))
-                usage(0, 1)
-        else:
-            infile = sys.stdin
-
-        val = infile.read()
-        if len(val) &gt; maxValueSize:
-            err("File is too big - please trim to %s" % maxValueSize)
-
-        if logVerbosity &gt;= 3:
-            sys.stderr.write("Inserting key - may take up to %s seconds\n" % (
-                timeout['findNode'] + timeout['store']))
-        res = client.put(key, val, local=localOnly)
-        if res == None:
-            err("Failed to insert '%s'" % key)
-            sys.exit(1)
-        else:
-            sys.exit(0)
-
-    elif cmd == 'addref':
-        if argc not in [0, 1]:
-            err("addref: bad argument count")
-            usage()
-
-        if argc == 1:
-            # try to open input file
-            path = args[0]
-            try:
-                infile = file(path, "rb")
-            except:
-                err("Cannot open input file %s" % repr(path))
-                usage(0, 1)
-        else:
-            infile = sys.stdin
-
-        ref = infile.read()
-
-        res = client.addref(ref)
-        if res == None:
-            err("Failed to add ref")
-            sys.exit(1)
-        else:
-            sys.exit(0)
-
-    elif cmd == 'getref':
-        if argc not in [0, 1]:
-            err("getref: bad argument count")
-            usage()
-
-        res = client.getref()
-
-        if argc == 1:
-            # try to open output file
-            path = args[0]
-            try:
-                outfile = file(path, "wb")
-            except:
-                err("Cannot open output file %s" % repr(path))
-                usage(0, 1)
-        else:
-            outfile = sys.stdout
-
-        if res == None:
-            err("Failed to retrieve node ref")
-            sys.exit(1)
-        else:
-            outfile.write(res)
-            outfile.flush()
-            outfile.close()
-            sys.exit(0)
-
-    elif cmd == 'pingall':
-        if logVerbosity &gt; 2:
-            print "Pinging all peers, waiting %s seconds for results" % timeout['ping']
-        res = client.pingall()
-        print res
-        sys.exit(0)
-
-</t>
-<t tx="aum.20040813211933">def test1():
-    """
-    A torturous test
-    """
-</t>
-<t tx="aum.20040813212609">def usage(detailed=False, ret=0):
-    
-    print "Usage: %s &lt;options&gt; [&lt;command&gt; [&lt;ars&gt;...]]" % sys.argv[0]
-    if not detailed:
-        print "Type %s -h for help" % sys.argv[0]
-        sys.exit(ret)
-
-    print "This is stasher, distributed file storage network that runs"
-    print "atop the anonymising I2P network (http://www.i2p.net)"
-    print "Written by aum - August 2004"
-    print
-    print "Options:"
-    print "  -h, --help              - display this help"
-    print "  -v, --version           - print program version"
-    print "  -V, --verbosity=n       - verbosity, default 1, 1=quiet ... 4=noisy"
-    print "  -S, --samaddr=host:port - host:port of I2P SAM port, "
-    print "                            default %s" % i2p.socket.samaddr
-    print "  -C, --clientaddr=host:port - host:port for socket interface to listen on"
-    print "                            for clients, default %s" % clientAddr
-    print "  -d, --datadir=dir       - directory in which stasher files get written"
-    print "                            default is ~/.stasher"
-    print "  -f, --foreground        - only valid for 'start' cmd - runs the node"
-    print "                            in foreground without spawning - for debugging"
-    print "  -l, --localonly         - only valid for get/put - restricts the get/put"
-    print "                            operation to the local node only"
-    print
-    print "Commands:"
-    print "  start [&lt;nodename&gt;]"
-    print "    - launches a single node, which forks off and runs in background"
-    print "      nodename is a short unique nodename, default is '%s'" % defaultNodename
-    print "  stop [&lt;nodename&gt;]"
-    print "    - terminates running node &lt;nodename&gt;"
-    print "  get &lt;keyname&gt; [&lt;file&gt;]"
-    print "    - attempts to retrieve key &lt;keyname&gt; from the network, saving"
-    print "      to file &lt;file&gt; if given, or to stdout if not"
-    print "  put &lt;keyname&gt; [&lt;file&gt;]"
-    print "    - inserts key &lt;keyname&gt; into the network, taking its content"
-    print "      from file &lt;file&gt; if given, otherwise reads content from stdin"
-    print "  addref &lt;file&gt;"
-    print "    - adds a new noderef to the node, taking the base64 noderef"
-    print "      from file &lt;file&gt; if given, or from stdin"
-    print "      (if you don't have any refs, visit http://stasher.i2p, or use"
-    print "      the dest in the file aum.stasher in cvs)"
-    print "  getref &lt;file&gt;"
-    print "    - uplifts the running node's dest as base64, writing it to file"
-    print "      &lt;file&gt; if given, or to stdout"
-    print "  hello"
-    print "    - checks that local node is running"
-    print "  pingall"
-    print "    - diagnostic tool - pings all peers, waits for replies or timeouts,"
-    print "      reports results"
-    print "  help"
-    print "    - display this help"
-    print
-
-    sys.exit(0)
-
-</t>
-<t tx="aum.20040813232532"></t>
-<t tx="aum.20040813232532.1">class KNodeServer(KBase, SocketServer.ThreadingMixIn, SocketServer.TCPServer):
-    """
-    Listens for incoming socket connections
-    """
-    @others
-</t>
-<t tx="aum.20040813232532.2">class KNodeReqHandler(KBase, SocketServer.StreamRequestHandler):
-    """
-    Manages a single client connection
-    """
-    @others
-</t>
-<t tx="aum.20040813232532.3">def handle(self):
-    """
-    Conducts all conversation for a single req
-    """
-    req = self.request
-    client = self.client_address
-    server = self.server
-    node = self.server.node
-
-    read = self.rfile.read
-    readline = self.rfile.readline
-    write = self.wfile.write
-    flush = self.wfile.flush
-    
-    finish = self.finish
-    
-    # start with a greeting
-    write("Stasher version %s ready\n" % version)
-    
-    # get the command
-    line = readline().strip()
-
-    try:
-        cmd, args = re.split("\\s+", line, 1)
-    except:
-        cmd = line
-        args = ''
-
-    self.log(3, "cmd=%s args=%s" % (repr(cmd), repr(args)))
-    
-    if cmd in ["get", "getlocal"]:
-        isLocal = cmd == "getlocal"
-        value = node.get(args, local=isLocal)
-        if value == None:
-            write("notfound\n")
-        else:
-            write("ok\n%s\n%s" % (len(value), value))
-        flush()
-        time.sleep(2)
-        finish()
-        return
-    
-    elif cmd in ["put", "putlocal"]:
-        isLocal = cmd == "putlocal"
-        try:
-            size = int(readline())
-            value = read(size)
-            res = node.put(args, value, local=isLocal)
-            if res:
-                write("ok\n")
-            else:
-                write("failed\n")
-            flush()
-        except:
-            traceback.print_exc()
-            write("exception\n")
-        finish()
-        return
-
-    elif cmd == 'addref':
-        try:
-            res = node.addref(args, True)
-            if res:
-                write("ok\n")
-            else:
-                write("failed\n")
-            flush()
-        except:
-            traceback.print_exc()
-            write("exception\n")
-        finish()
-        return
-
-    elif cmd == 'getref':
-        res = node.dest
-        write("ok\n")
-        write("%s\n" % res)
-        flush()
-        time.sleep(1)
-        finish()
-        return
-
-    elif cmd == 'pingall':
-        res = node._pingall()
-        write(res+"\n")
-        finish()
-        return
-
-    elif cmd == "die":
-        server.isRunning = False
-        write("server terminated\n")
-        finish()
-
-    else:
-        write("unrecognisedcommand\n")
-        finish()
-        return
-
-</t>
-<t tx="aum.20040813233036">def __init__(self, node, addr=None):
-
-    if addr == None:
-        addr = clientAddr
-
-    self.isRunning = True
-
-    self.node = node
-
-    listenHost, listenPort = addr.split(":")
-    listenPort = int(listenPort)
-    self.listenPort = listenPort
-    SocketServer.TCPServer.__init__(self, (listenHost, listenPort), KNodeReqHandler)
-
-</t>
-<t tx="aum.20040813234004"></t>
-<t tx="aum.20040813234004.1">def serve(self):
-    """
-    makes this node listen on socket for incoming client
-    connections, and services these connections
-    """
-    server = KNodeServer(self)
-    server.serve_forever()
-
-</t>
-<t tx="aum.20040813234214">def serve_forever(self):
-
-    print "awaiting client connections on port %s" % self.listenPort
-    while self.isRunning:
-        self.handle_request()
-
-</t>
-<t tx="aum.20040814001156">class KNodeClient(KBase):
-    """
-    Talks to a KNodeServer over a socket
-    
-    Subclass this to implement Stasher clients in Python
-    """
-    @others
-
-</t>
-<t tx="aum.20040814001156.1">def __init__(self, address=clientAddr):
-
-    if type(address) in [type(()), type([])]:
-        self.host, self.port = clientAddr
-    else:
-        self.host, self.port = clientAddr.split(":")
-        self.port = int(self.port)
-
-    self.hello()
-
-</t>
-<t tx="aum.20040814001456">def connect(self):
-
-    self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    self.sock.connect((self.host, self.port))
-
-    self.rfile = self.sock.makefile("rb")
-    self.read = self.rfile.read
-    self.readline = self.rfile.readline
-    self.wfile = self.sock.makefile("wb")
-    self.write = self.wfile.write
-    self.flush = self.wfile.flush
-
-    # read greeting
-    greeting = self.readline()
-    parts = re.split("\\s+", greeting)
-    if parts[0] != "Stasher":
-        self.close()
-        raise Exception("Not connected to valid stasher interface")
-
-</t>
-<t tx="aum.20040814001456.2">def get(self, key, **kw):
-    """
-    sends a get command to stasher socket, and retrieves
-    and interprets result
-    
-    Arguments:
-        - key - key to retrieve
-    
-    Keywords:
-        - local - default False - if True, only looks in local storage
-    
-    Returns key's value if found, or None if key not found
-    """
-    if kw.get('local', False):
-        cmd = 'getlocal'
-    else:
-        cmd = 'get'
-
-    self.connect()
-    
-    self.write("%s %s\n" % (cmd, key))
-    self.flush()
-
-    #print "waiting for resp line"
-    res = self.readline().strip()
-
-    if res == "ok":
-        size = int(self.readline())
-        val = self.read(size)
-        self.close()
-        return val
-    else:
-        self.close()
-        return None
-
-</t>
-<t tx="aum.20040814001522">def close(self):
-    
-    self.rfile.close()
-    #self.wfile.close()
-    self.sock.close()
-
-</t>
-<t tx="aum.20040814001912">def hello(self):
-    
-    self.connect()
-    self.close()
-</t>
-<t tx="aum.20040814002236">def put(self, key, val, **kw):
-    """
-    Tells remote stasher port to insert a file into the network
-
-    Arguments:
-        - key - key to insert under
-        - val - value to insert under this key
-    
-    Keywords:
-        - local - default False - if True, only looks in local storage
-    
-    """
-    if kw.get('local', False):
-        cmd = 'putlocal'
-    else:
-        cmd = 'put'
-
-    self.connect()
-    self.write("%s %s\n" % (cmd, key))
-    self.write("%s\n" % len(val))
-    self.write(val)
-    self.flush()
-    
-    res = self.readline().strip()
-
-    self.close()
-
-    if res == "ok":
-        return True
-    else:
-        print repr(res)
-        return False
-
-</t>
-<t tx="aum.20040814002411">def __getitem__(self, item):
-    
-    return self.get(item)
-
-</t>
-<t tx="aum.20040814002411.1">def __setitem__(self, item, val):
-    
-    if not self.put(item, val):
-        raise Exception("Failed to insert")
-
-</t>
-<t tx="aum.20040814003559">def kill(self):
-    """
-    Tells remote server to fall on its sword
-    """
-    try:
-        while 1:
-            self.connect()
-            self.write("die\n")
-            self.flush()
-            self.close()
-    except:
-        pass
-
-
-</t>
-<t tx="aum.20040814004432">def finish(self):
-
-    SocketServer.StreamRequestHandler.finish(self)
-
-</t>
-<t tx="aum.20040814015747">def err(msg):
-    sys.stderr.write(msg+"\n")
-</t>
-<t tx="aum.20040814103533">def addref(self, ref):
-    """
-    Passes a new noderef to node
-    """
-    self.connect()
-    self.write("addref %s\n" % ref)
-    self.flush()
-    
-    res = self.readline().strip()
-
-    self.close()
-
-    if res == "ok":
-        return True
-    else:
-        print repr(res)
-        return False
-
-</t>
-<t tx="aum.20040814110540">def userI2PDir(nodeName=None):
-    """
-    Returns a directory under user's home dir into which
-    stasher files can be written
-
-    If nodename is given, a subdirectory will be found/created
-    
-    Return value is toplevel storage dir if nodename not given, 
-    otherwise absolute path including node dir
-    """
-    if dataDir != None:
-        if not os.path.isdir(dataDir):
-            os.makedirs(dataDir)
-        return dataDir
-
-    if sys.platform == 'win32':
-        home = os.getenv("APPDATA")
-        if home:
-            topDir = os.path.join(home, "stasher")
-        else:
-            topDir = os.path.join(os.getcwd(), "stasher")
-    else:
-        #return os.path.dirname(__file__)
-        topDir = os.path.join(os.path.expanduser('~'), ".stasher")
-
-    if not os.path.isdir(topDir):
-        os.makedirs(topDir)
-    if nodeName == None:
-        return topDir
-    else:
-        nodeDir = os.path.join(topDir, nodeName)
-        if not os.path.isdir(nodeDir):
-            os.makedirs(nodeDir)
-        return nodeDir
-
-</t>
-<t tx="aum.20040814110755">def nodePidfile(nodename):
-    return os.path.join(userI2PDir(nodename), "node.pid")
-
-</t>
-<t tx="aum.20040814112703">def spawnproc(*args, **kw):
-    """
-    Spawns a process and returns its PID
-    
-    VOMIT!
-    
-    I have to do a pile of odious for the win32 side
-    
-    Returns a usable PID
-    
-    Keywords:
-        - priority - priority at which to spawn - default 20 (highest)
-    """
-    # get priority, convert to a unix 'nice' value
-    priority = 20 - kw.get('priority', 20)
-    
-    if sys.platform != 'win32':
-        # *nix - easy
-        #print "spawnproc: launching %s" % repr(args)
-        
-        # insert nice invocation
-        args = ['/usr/bin/nice', '-n', str(priority)] + list(args)
-        return os.spawnv(os.P_NOWAIT, args[0], args)
-
-    else:
-        # just close your eyes here and pretend this abomination isn't happening! :((
-        args = list(args)
-        args.insert(0, sys.executable)
-        cmd = " ".join(args)
-        #print "spawnproc: launching %s" % repr(cmd)
-
-        if 0:
-            try:
-                c =  _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE)
-                c1 = _winreg.OpenKey(c, "SOFTWARE")
-                c2 = _winreg.OpenKey(c1, "Microsoft")
-                c3 = _winreg.OpenKey(c2, "Windows NT")
-                c4 = _winreg.OpenKey(c3, "CurrentVersion")
-                supportsBelowNormalPriority = 1
-            except:
-                supportsBelowNormalPriority = 0
-        else:
-            if sys.getwindowsversion()[3] != 2:
-                supportsBelowNormalPriority = 0
-            else:
-                supportsBelowNormalPriority = 1
-    
-        # frig the priority into a windows value
-        if supportsBelowNormalPriority:
-            if priority &lt; 7:
-                pri = win32process.IDLE_PRIORITY_CLASS
-            elif priority &lt; 14:
-                pri = 0x4000
-            else:
-                pri = win32process.NORMAL_PRIORITY_CLASS
-        else:
-            if priority &lt; 11:
-                pri = win32process.IDLE_PRIORITY_CLASS
-            else:
-                pri = win32process.NORMAL_PRIORITY_CLASS
-    
-        print "spawnproc: launching %s" % repr(args)
-        si = win32process.STARTUPINFO()
-        hdl = win32process.CreateProcess(
-            sys.executable, # lpApplicationName
-            cmd,  # lpCommandLine
-            None,  # lpProcessAttributes
-            None, # lpThreadAttributes
-            0,    # bInheritHandles
-            0,    # dwCreationFlags
-            None, # lpEnvironment
-            None, # lpCurrentDirectory
-            si,   # lpStartupInfo
-            )
-        pid = hdl[2]
-        #print "spawnproc: pid=%s" % pid
-        return pid
-</t>
-<t tx="aum.20040814112703.1">def killproc(pid):
-    if sys.platform == 'win32':
-        print repr(pid)
-        handle = win32api.OpenProcess(1, 0, pid)
-        print "pid %s -&gt; %s" % (pid, repr(handle))
-        #return (0 != win32api.TerminateProcess(handle, 0))
-        win32process.TerminateProcess(handle, 0)
-    else:
-        return os.kill(pid, signal.SIGKILL)
-</t>
-<t tx="aum.20040814120624">def i2psocket(self, *args, **kw):
-    return i2p.socket.socket(*args, **kw)
-
-</t>
-<t tx="aum.20040814131117">def getref(self):
-    """
-    Uplifts node's own ref
-    """
-    self.connect()
-    self.write("getref\n")
-    self.flush()
-    
-    res = self.readline().strip()
-
-    if res == "ok":
-        ref = self.readline().strip()
-        self.close()
-        return ref
-    else:
-        self.close()
-        return "failed"
-
-</t>
-<t tx="aum.20040814132559">@first #! /usr/bin/env python
-@others
-</t>
-<t tx="aum.20040814133042">@first #! /bin/sh
-
-export I2PPYDIR=/main/i2p/cvs/i2p/apps/stasher/python
-export WEBDIR=/main/i2p/services/stasher.i2p
-export CVSDIR=/main/i2p/cvs/i2p/apps/stasher/python
-
-cp README.txt $I2PPYDIR
-cp stasher.py $I2PPYDIR/src
-cp bencode.py $I2PPYDIR/src
-cp code.leo $I2PPYDIR/src
-cp *.stasher $I2PPYDIR/noderefs
-cp *.stasher $WEBDIR/noderefs
-
-cp stasher $I2PPYDIR/scripts
-cp stasher-launch.py $I2PPYDIR/scripts/stasher.py
-
-cp setup-stasher.py $I2PPYDIR/setup.py
-
-# generate API dco
-epydoc -n "Stasher Python API" -o api stasher.py
-
-# make a release tarball
-
-rm -rf release/*
-export STVERSION=`./stasher.py --shortversion`
-export TARDIR=release/stasher-$STVERSION
-export DIRNAME=stasher-$STVERSION
-export TARBALLNAME=stasher.tar.gz
-mkdir $TARDIR
-
-cp -a /main/i2p/cvs/i2p/apps/sam/python/i2p $TARDIR
-cp -a code.leo stasher stasher.py bencode.py api $TARDIR
-cp README-tarball.txt $TARDIR/README.txt
-
-mkdir $TARDIR/noderefs
-cp *.stasher $TARDIR/noderefs
-
-cd release
-
-tar cfz $TARBALLNAME $DIRNAME
-
-# copy tarball and doco to websites
-cp $TARBALLNAME $WEBDIR
-cd ..
-cp -a api $WEBDIR
-
-# last but not least, commit to cvs
-cp stasher.py $CVSDIR/src
-cp *.stasher $CVSDIR/noderefs
-cd $CVSDIR
-cvs commit
-</t>
-<t tx="aum.20040814134235">@others
-</t>
-<t tx="aum.20040814140643">@first #! /usr/bin/env python
-# wrapper script to run stasher node
-
-# set this to the directory where you've installed stasher
-stasherDir = "/path/to/my/stasher/dir"
-
-import sys
-sys.path.append(stasherDir)
-import stasher
-stasher.main()
-</t>
-<t tx="aum.20040814191506">@first #! /bin/sh
-rm -rf /tmp/node1
-stasher -V4 -Slocalhost:7656 -Clocalhost:7659 -d/tmp/node1 _start node1
-
-</t>
-<t tx="aum.20040814191718">@first #! /bin/sh
-rm -rf /tmp/node2
-stasher -V4 -Slocalhost:17656 -Clocalhost:17659 -d/tmp/node2 _start node2
-
-</t>
-<t tx="aum.20040814234015">@first #! /usr/bin/env python
-"""
-This is the installation script for Stasher, a distributed
-file storage framework for I2P.
-"""
-
-import sys, os
-from distutils.core import setup
-
-oldcwd = os.getcwd()
-os.chdir("src")
-
-if sys.platform == 'win32':
-    stasherScript = "..\\scripts\\stasher.py"
-else:
-    stasherScript = "../scripts/stasher"
-
-
-try:
-    import i2p
-    import i2p.socket
-    import i2p.select
-except:
-    print "Sorry, but you don't seem to have the core I2P"
-    print "python library modules installed."
-    print "If you're installing from cvs, please go to"
-    print "i2p/apps/sam/python, become root, and type:"
-    print "  python setup.py install"
-    print "Then, retry this installation."
-    sys.exit(1)
-
-setup(name="Stasher",
-      version="0.0",
-      description="Kademlia-based P2P distributed file storage app for I2P",
-      author="aum",
-      author_email="aum_i2p@hotmail.com",
-      url="http://stasher.i2p",
-      py_modules = ['stasher', 'bencode'],
-      scripts = [stasherScript],
-     )</t>
-<t tx="aum.20040815000938"></t>
-<t tx="aum.20040815000938.1"></t>
-<t tx="aum.20040815000938.2"></t>
-<t tx="aum.20040815001048">@nocolor
-STASHER README
-
-@others
-</t>
-<t tx="aum.20040815143009">@nocolor
-STASHER README
-
-@others
-</t>
-<t tx="aum.20040815143611">-----------------------
-INSTALLING STASHER
-
-Prerequisite:
-
-Before you can install/run Stasher, you will first need to have installed
-the I2P Python modules - available in cvs at i2p/apps/sam/python.
-
-To install stasher, just make sure you've got the latest cvs, then type
-  python setup.py install
-as root.
-
-This installs the stasher engine, plus a wrapper client script called
-'stasher', which setup.py will install into your execution path.
-
-If you don't like the thought of becoming root, you could just put stasher.py
-on your execution path, and/or create a symlink called 'stasher'.
-
-Test your installation by typing 'stasher -h' - this should display
-a help message.
-
-</t>
-<t tx="aum.20040815143611.1">------------------------
-DOZE USERS PLEASE NOTE
-
-You'll need to watch and see where the stasher.py
-wrapper script gets installed. On my box, it ends up on d:\python23\scripts,
-but on your box it'll likely go into c:\python23\scripts.
-
-You may either update your system PATH environment variable to include your
-python scripts directory, OR, you can copy stasher.py to anywhere that's
-on your path.
-
-In the explanations below, note that wherever I say to type 'stasher', you'll
-need to type 'stasher.py' instead.
-
-</t>
-<t tx="aum.20040815143611.2">------------------------
-WARNING
-
-This is a very early pre-alpha test version of stasher.
-It is only capable of storing or retrieving files of
-less than 29k in size.
-
-Also, files are totally insecure - anyone can overwrite any keys you
-insert, and vice versa.
-
-I'll be adding support for CHK-like and SSK-like keys in due course.
-
-</t>
-<t tx="aum.20040815143611.3">------------------------
-USING STASHER
-
-To see stasher's options, type:
-
-  stasher -h
-
-This should dump out a verbose help message.
-
-To start a stasher node, type:
-
-  stasher start
-
-To shut down a stasher node, type:
-
-  stasher stop
-
-To insert a file into stasher, type:
-
-  stasher put mykey myfile
-
-Note, if you don't supply a filename, stasher can read
-the file from standard input.
-
-To retrieve a file from stasher, type:
-
-  stasher get mykey
-
-</t>
-<t tx="aum.20040815143611.4">INSTALLING STASHER FROM THE TARBALL
-
-For regular users:
-
- 1. Crack this tarball, and copy the 'stasher-n.n.n' directory
-    somewhere safe, such as your home directory
-
- 2. Edit the small 'stasher' script therein, and set the variable
-    'stasherDir' as directed
-
- 3. Either put this directory onto your PATH, or create a symlink
-    called 'stasher' within any of your PATH dirs, pointing to
-    the stasher script
-
- 3. Test your installation by typing:
-          stasher -v
-
-For windows users:
-
- 1. Make sure you have python2.3 or later installed
-
- 2. Untar this directory, and copy the directory into
-    C:\Program Files, or wherever you like to put your appz
-
- 3. Wherever you put the directory, add that to your system-wide
-    PATH environment variable
-
- 4. Test your installation by opening up an ugly black DOS window,
-    and typing:
-           stasher.py -v
-
- 5. Note - in the USAGE directions below, instead of typing 'stasher',
-    you'll need to type 'stasher.py'
-
-</t>
-<t tx="aum.20040815164410"></t>
-<t tx="aum.20040815164410.1">class KRpcPingAll(KRpc):
-    """
-    Pings all peers
-    """
-    @others
-</t>
-<t tx="aum.20040815164410.2">type = 'pingall'
-</t>
-<t tx="aum.20040815164410.3">def __init__(self, localNode, client=None, **kw):
-    """
-    Creates and launches a PINGALL rpc
-
-    Arguments:
-        - localNode - the node performing this RPC
-        - client - see KRpc.__init__
-
-    Keywords: none
-    """
-    if kw.has_key('cbArgs'):
-        KRpc.__init__(self, localNode, client, cbArgs=kw['cbArgs'])
-    else:
-        KRpc.__init__(self, localNode, client)
-
-</t>
-<t tx="aum.20040815164410.4">def start(self):
-    """
-    Kicks off this RPC
-    """
-    # launch a findNode rpc against each of our peers
-    peers = self.localNode.peers
-    self.numSent = self.numPending = len(peers)
-    self.numReplied = self.numFailed = 0
-    for peer in peers:
-        KRpcPing(self.localNode, self.on_reply, peer=peer)
-    return
-
-</t>
-<t tx="aum.20040815164410.5">def returnValue(self, result):
-    """
-    an override with a nicer call sig
-    """
-    # a hack for testing - save this RPC object into the node
-    # so we can introspect it
-    self.localNode.lastrpc = self
-
-    try:
-        KRpc.returnValue(self, result, status=result)
-    except:
-        traceback.print_exc()
-        self.log(3, "Failed to return %s" % repr(result))
-        KRpc.returnValue(self, 0, status=0)
-
-</t>
-<t tx="aum.20040815164410.7">def on_reply(self, result):
-    """
-    callback which fires when we get a reply from a STORE we sent to a
-    peer
-    """
-    log(3, "got %s" % repr(result))
-
-    if result:
-        self.numReplied += 1
-    else:
-        self.numFailed += 1
-    self.numPending -= 1
-
-    if self.numPending &lt;= 0:
-        res = "pinged:%s replied:%s timeout:%s" % (
-                self.numSent, self.numReplied, self.numFailed)
-        self.log(3, res)
-        self.returnValue(res)
-
-</t>
-<t tx="aum.20040815164410.8">def on_tick(self):
-
-    self.log(3, "this shouldn't have happened")
-    self.returnValue(False)
-
-</t>
-<t tx="aum.20040815170327">def _pingall(self, callback=None):
-    """
-    Sends a ping to all peers, returns text string on replies/failures
-    """
-    if callback:
-        KRpcPingAll(self, callback, **kw)
-    else:
-        return KRpcPingAll(self).execute()
-
-   
-</t>
-<t tx="aum.20040815170456">def pingall(self):
-    """
-    Uplifts node's own ref
-    """
-    self.connect()
-    self.write("pingall\n")
-    self.flush()
-    
-    res = self.readline().strip()
-
-    self.close()
-
-    return res
-
-
-</t>
-<t tx="aum.20040816014757">def returnValue(self, items):
-    """
-    override with a nicer call sig
-    """
-    # a hack for testing - save this RPC object into the node
-    # so we can introspect it
-    self.localNode.lastrpc = self
-
-    # another debugging hack
-    self.reportStats()
-
-    KRpc.returnValue(self, items, result=items)
-
-</t>
-<t tx="aum.20040816133040">def storeSplit(self):
-    """
-    Gets called if we're splitting a big file into smaller chunks
-    
-    Here, we:
-        - break the file up into chunks
-        - build a manifest
-        - launch store RPCs to store each chunk, where the key is SHA(chunk)
-        - launch a store RPC to store the 'manifest' (noting that if the manifest
-          is too big, it'll get recursively inserted as a splitfile as well
-    """
-    # break up into chunks
-    chunks = []
-    hashes = []
-    size = len(self.value)
-    i = 0
-    self.nchunks = 0
-    while i &lt; size:
-        chunks.append(self.value[i:i+maxValueSize])
-        hashes.append(shahash(chunks[-1]))
-        i += maxValueSize
-        self.nchunks += 1
-
-    # build the manifest
-    manifest = "chunks:%s\n%s\n" % (self.nchunks, "\n".join(hashes))
-    
-    # set progress attributes
-    self.chunkManifestInserted = False
-    self.chunksInserted = 0
-
-    # launch nested Store RPCs for manifest, and each chunk
-    KRpcStore(self.localNode, self.on_doneChunkManifest,
-              local=self.isLocalOnly,
-              key=self.key,
-              value=manifest)
-    i = 0
-    while i &lt; self.nchunks:
-        KRpcStore(self.localNode, self.on_doneChunk,
-                  local=self.isLocalOnly,
-                  key=hashes[i],
-                  value=chunks[i])
-        i += 1
-
-    # now sit back and wait for the callbacks
-</t>
-<t tx="aum.20040816135222">def on_doneChunkManifest(self, result):
-    """
-    Callback which fires when a manifest insert succeeds/fails
-    """
-    # the chunk callback handles all
-    self.on_doneChunk(result, isManifest=True)
-</t>
-<t tx="aum.20040816135222.1">def on_doneChunk(self, result, isManifest=False):
-    """
-    Callback which fires when a single chunk insert succeeds/fails
-    """
-    # a failure either way means the whole RPC has failed
-    if not result:
-        # one huge fuck-up
-        self.returnValue(False)
-        return
-
-    # update our tally
-    if isManifest:
-        self.chunkManifestInserted = True
-    else:
-        self.chunksInserted += 1
-
-    # finished?
-    if self.chunkManifestInserted and (self.chunksInserted == self.nchunks):
-        # yep = success
-        self.returnValue(True)
-
-</t>
-<t tx="aum.20040816141128">def on_gotValue(self, value, hash=None):
-    """
-    Callback which fires when we get the value stored under a key
-    
-    Value is either the real value, or a splitfile manifest
-    If a real value, just return it.
-    If a splitfile manifest, launch nested findValue RPCs to get each chunk
-    """
-    nchunks = 0
-    try:
-        firstline, rest = value.split("\n", 1)
-        firstline = firstline.strip()
-        kwd, str_nchunks = firstline.split(":")
-        if kwd != 'chunks':
-            raise hell
-        nchunks = int(nchunks)
-        value = rest
-    except:
-        pass # in this case, hell hath no fury at all
-    
-    if nchunks == 0:
-        self.returnValue(value)
-        return
-
-    # now we get to the hard bit - we have to set up nested findData RPCs to
-    # get all the chunks and reassemble them
-    hashes = rest.strip().split("\n")
-    
-    # do sanity checks
-    hashesAllValid = [len(h) == 40 for h in hashes]
-    if len(hashes) != nchunks:
-        self.log(
-            2,
-            "Splitfile retrieval failure\nmanifest contains %s hashes, should have been %s" % (
-                len(hashes), nchunks))
-        self.returnValue(None)
-    if False in hashesAllValid:
-        self.log(2, "Splitfile retrieval failure - one or more invalid hashes")
-
-    # now this is a bit weird - we need to bind each chunk to its hash, so we create a
-    # class which produces callables which fire our on_gotChunk callback
-    class ChunkNotifier:
-        def __init__(me, h, cb):
-            me.h = h
-            me.cb = cb
-        def __call__(me, val):
-            me.cb(me.h, val)
-    
-    # now launch the chunk retrieval RPCs
-    # result is that for each retrieved chunk, our on_gotChunk callback will
-    # be invoked with the arguments (hash, value), so we can tick them off
-    self.numChunks = nchunks
-    self.numChunksReceived = 0
-    self.chunkHashes = hashes
-    self.chunks = dict.fromkeys(hashes)
-    for h in hashes:
-        KRpcFindData(self.localNode, h, ChunkNotifier(h, self.on_gotChunk))
-
-    # now, we can sit back and receive the chunks
-
-</t>
-<t tx="aum.20040816141128.1">def on_gotChunk(self, hexhash, value):
-    """
-    Callback which fires when a nested chunk findNode returns
-    """
-    if value == None:
-        self.log(2, "Chunk retrieval failed, fatal to this findData")
-        self.returnValue(None)
-        return
-    
-    # got a value - vet it against hash
-    if shahash(value) != hexhash:
-        self.log(2, "Got a chunk, but it doesn't hash right - fatal to this findData")
-        self.returnValue(None)
-        return
-
-    # it's valid - stash it
-    self.chunks[hexhash] = value
-    self.numChunksReceived += 1
-
-    # have we finished yet?
-    if self.numChunksReceived &lt;= self.numChunks:
-        # no
-        self.log(4, "Received chunk %s of %s" % (self.numChunksReceived, self.numChunks))
-        return
-
-    # maybe we have
-    self.log(4, "We appear to have all chunks, checking further")
-
-    # sanity check
-    if None in self.chunks.values():
-        self.log(2, "Fatal - reached chunk count, but chunks still missing")
-        self.returnValue(None)
-        return
-            
-    # finally done - got all chunks, hashes are valid, reassemble in order
-    allChunks = [self.chunks[h] for h in self.chunkHashes]
-    reassembled = "".join(allChunks)
-    self.log(4, "Reassembled all %s chunks, SUCCESS" % self.numChunks)
-    self.returnValue(reassembled)
-
-</t>
-</tnodes>
-</leo_file>
diff --git a/apps/stasher/python/src/stasher.py b/apps/stasher/python/src/stasher.py
deleted file mode 100644
index d007282a2914952079769a916d645c109517ae50..0000000000000000000000000000000000000000
--- a/apps/stasher/python/src/stasher.py
+++ /dev/null
@@ -1,4416 +0,0 @@
-#! /usr/bin/env python
-#@+leo-ver=4
-#@+node:@file stasher.py
-#@@first
-"""
-A simple implementation of the
-U{Kademlia<http://www.infoanarchy.org/wiki/wiki.pl?Kademlia>}
-P2P distributed storage and retrieval protocol, designed to
-utilise the U{I2P<http://www.i2p.net>} stealth network as its transport.
-
-Most application developers will only need to know about the L{KNode} class
-"""
-
-# I strongly recommend that when editing this file, you use the Leo
-# outlining and literate programming editor - http://leo.sf.net
-# If Leo doesn't agree with your religion, please try to leave the markups intact
-
-#@+others
-#@+node:explanatory comments
-#@+at
-# Tech overview:
-#     - this implementation creates each Node ID as an SHA1 hash of
-#       the node's 'destination' - the string which constitutes its
-#       address as an I2P endpoint.
-# 
-# Datagram formats:
-#     - each datagram sent from one node to another is a python dict object,
-#       encoded and decoded with the 'bencode' object serialisation module.
-#     - we use bencode because regular Python pickle is highly insecure,
-#       allowing crackers to create malformed pickles which can have all
-#       manner of detrimental effects, including execution of arbitrary code.
-#     - the possible messages are listed below, along with their consituent
-#       dictionary keys:
-#           1. ping:
-#               - msgId - a message identifier guaranteed to be unique
-#                 with respect to the sending node
-#           2. findNode:
-#               - msgId - unique message identifier
-#               - hash - the hash we're looking for
-#               - initiator - True/False, according to whether this node
-#                 should initiate/perform the findNode, or whether this
-#                 rpc is coming from another seeking node
-#           3. findData:
-#               - msgId - unique message identifier
-#               - hash - the exact key hash of the data we want to retrieve
-#               - initiator - True/False, according to whether this node
-#                 should initiate/perform the findNode, or whether this
-#                 rpc is coming from another seeking node
-#           4. store:
-#               - msgId - unique message identifier
-#               - hash - the exact key hash of the data we want to store
-#               - data - the data we want to store
-#           5. reply:
-#               - msgId - the original msgId we're replying to
-#              The other items in a reply message depend on what kind
-#              of message we're replying to, listed below:
-#                     1. ping - no additional data
-#                     2. findNode:
-#                         - nodes - a list of dests nearest the given hash
-#                     3. findData:
-#                         - nodes - as for findNode, OR
-#                         - data - the retrieved data, or None if not found
-#                     4. store:
-#                         - status - True or False according to whether
-#                           the store operation was successful
-#@-at
-#@-node:explanatory comments
-#@+node:imports
-import sys, os, types, sha, random, threading, thread, traceback, Queue
-import time, math, random, pickle, getopt, re
-import signal
-
-# some windows-specifics (yggghh)
-if sys.platform == 'win32':
-    try:
-        import win32api
-        import win32process
-        import _winreg
-    except:
-        print "Python win32 extensions not installed."
-        print "Please go to http://sourceforge.net/project/showfiles.php?group_id=78018"
-        print "and download/install the file pywin32-202.win32-py%s.%s.exe" % \
-            sys.version_info[:2]
-        sys.exit(1)
-
-from StringIO import StringIO
-from pdb import set_trace
-
-try:
-    import bencode
-except:
-    print "The bencode module is missing from your python installation."
-    print "Are you sure you installed Stasher correctly?"
-    sys.exit(1)
-
-try:
-    import i2p.socket
-    import i2p.select
-    import i2p.pylib
-    SocketServer = i2p.pylib.SocketServer
-    socket = i2p.pylib.socket
-except:
-    print "You don't appear to have the I2P Python modules installed."
-    print "Not good. Stasher totally needs them."
-    print "Please to to i2p/apps/sam/python in your I2P cvs tree, and"
-    print "install the core I2P python modules first"
-    sys.exit(1)
-
-#@-node:imports
-#@+node:constants
-
-# --------------------------------------------
-# START USER-CONFIGURABLE CONSTANTS
-# --------------------------------------------
-
-# host:port to connect to I2P SAM Bridge
-samAddr = i2p.socket.samaddr
-
-# host:port to listen on for command line client
-clientAddr = "127.0.0.1:7659"
-
-defaultNodename = "0" # will be prefixed by 'stashernode'
-
-# maximum size of each stored item
-maxValueSize = 30000
-
-# maximum number of noderefs that can be stored in a bucket
-# (refer spec section 2.1, first paragraph)
-maxBucketSize = 20
-
-# number of peers to return from a search
-numSearchPeers = 3
-
-# maximum number of concurrent queries per findnode/finddata rpc
-maxConcurrentQueries = 10
-
-# number of peers to store onto
-numStorePeers = 10
-
-# Logger settings
-logFile = None
-logVerbosity = 2
-
-# data directory location - set to a path to override the default
-# which is the user's home dir
-dataDir = None
-
-# whether a node, on startup, should do a findnode on itself to
-# locate its closest neighbours
-greetPeersOnStartup = False
-#greetPeersOnStartup = True
-
-# multi-purpose testing flag
-testing = False
-#testing = True
-
-tunnelDepth = 0
-
-# set to True to enable single handler thread that manages all nodes,
-# or False to make each node run its own handler thread
-#runCore = False
-runCore = True
-
-# timeouts - calibrate as needed
-timeout = {
-    'ping' : 120,
-    'findNode' : 120,
-    'findData' : 120,
-    'store' : 120,
-    }
-
-logToSocket = None
-
-desperatelyDebugging = False
-
-if desperatelyDebugging:
-    runCoreInBackground = False
-else:
-    runCoreInBackground = True
-
-# --------------------------------------------
-# END OF USER-CONFIGURABLE CONSTANTS
-# --------------------------------------------
-
-# ----------------------------------------------
-# hack anything below this line at your own risk
-
-#@-node:constants
-#@+node:globals
-# keep a dict of existing nodes, so we can prevent
-# client progs from creating 2 nodes of the same name
-_nodes = {}
-
-version = "0.0.1"
-
-#@-node:globals
-#@+node:Exceptions
-# define our exceptions
-
-class KValueTooLarge(Exception):
-    """
-    Trying to insert a value of excessive size into the network.
-    Maximum key size is L{maxValueSize}
-    """
-
-class KBadHash(Exception):
-    """
-    Invalid hash string
-    """
-
-class KNotImplemented(Exception):
-    """
-    A required method was not implemented
-    """
-
-class KBadNode(Exception):
-    """
-    Invalid Node object
-    """
-
-class KBadPeer(Exception):
-    """
-    Invalid Peer object - should be a KPeer
-    """
-
-class KBadDest(Exception):
-    """Invalid I2P Node Dest"""
-
-#@-node:Exceptions
-#@+node:Mixins
-#@+node:class KBase
-class KBase:
-    """
-    A mixin which adds a class-specific logger
-    """
-    def log(self, verbosity, msg):
-        
-        log(verbosity, msg, 1, self.__class__.__name__)
-
-    def logexc(self, verbosity, msg):
-
-        logexc(verbosity, msg, 1, self.__class__.__name__)
-
-#@-node:class KBase
-#@-node:Mixins
-#@+node:Main Engine
-#@+node:class KCore
-class KCore(KBase):
-    """
-    Singleton class which performs all the needed background processing.
-    
-    By scheduling all processing through this object, we eliminate the
-    need to create threads on a per-node basis, and also make this thing
-    far easier to debug.
-
-    The core launches only two background threads:
-        - L{threadRxPackets} - listen for incoming packets bound for
-          any node running within a single process
-        - L{threadHousekeeping} - periodically invoke maintenance methods
-          of each node, so the node can check for timeout conditions and
-          other untoward happenings
-
-    These threads start up when the first node in this process is created,
-    and stop when the last node ceases to exist.
-
-    Upon first import, the L{stasher} module creates one instance of this
-    class. Upon creation, L{KNode} objects register themselves with this core.
-    """
-    #@    @+others
-    #@+node:attributes
-    #@-node:attributes
-    #@+node:__init__
-    def __init__(self, bg=True):
-        """
-        Creates the I2P Kademlia core object
-        """
-        self.bg = bg
-        self.fg = False
-    
-        # subscribed nodes
-        self.nodes = []
-        #self.nodesLock = threading.Lock()
-        
-        self.isRunning = False
-        self.isRunning_rx = False
-    
-    #@-node:__init__
-    #@+node:subscribe
-    def subscribe(self, node):
-        """
-        Called by a node to 'subscribe' for background processing
-        If this is the first node, starts the handler thread
-        """
-        #self.nodesLock.acquire()
-        try:
-            nodes = self.nodes
-            
-            if node in nodes:
-                self.log(2, "duhhh! node already subscribed" % repr(node))
-                return
-        
-            nodes.append(node)
-            
-            if not self.isRunning:
-                self.isRunning = True
-                if self.bg and not self.fg:
-                    self.log(3, "First node subscribing, launching threads")
-                    thread.start_new_thread(self.threadRxPackets, ())
-                    thread.start_new_thread(self.threadHousekeeping, ())
-        except:
-            traceback.print_exc()
-            self.log(2, "exception")
-    
-        #self.nodesLock.release()
-    
-    #@-node:subscribe
-    #@+node:unsubscribe
-    def unsubscribe(self, node):
-        """
-        Unsubscribes a node from the core
-        
-        If this was the last node, stops the handler thread
-        """
-        #self.nodesLock.acquire()
-        try:
-            nodes = self.nodes
-            
-            if node not in nodes:
-                self.log(4, "duhhh! node %s was not subscribed" % repr(node))
-                return
-    
-            self.log(2, "trying to unsubscribe node %s" % node.name)
-            nodes.remove(node)
-    
-            if len(nodes) == 0:
-                self.isRunning = False
-        except:
-            traceback.print_exc()
-            self.log(2, "exception")
-    
-        #self.nodesLock.release()
-    
-    #@-node:unsubscribe
-    #@+node:threadRxPackets
-    def threadRxPackets(self):
-        """
-        Sits on a select() loop, processing incoming datagrams
-        and actioning them appropriately.
-        """
-        self.isRunning_rx = True
-        self.log(3, "KCore packet receiver thread running")
-        try:
-            while self.isRunning:
-                socks = [node.sock for node in self.nodes]
-                if desperatelyDebugging:
-                    set_trace()
-                try:
-                    inlist, outlist, errlist = self.select(socks, [], [], 1)
-                except KeyboardInterrupt:
-                    self.isRunning = 0
-                    return
-    
-                self.log(5, "\ninlist=%s" % repr(inlist))
-                if inlist:
-                    self.log(5, "got one or more sockets with inbound data")
-                    #self.nodesLock.acquire()
-                    for sock in inlist:
-                        node = self.nodeWhichOwnsSock(sock)
-                        if node != None:
-                            node._doRx()
-                    #self.nodesLock.release()
-    
-                elif self.fg:
-                    return
-    
-                else:
-                    time.sleep(0.1)
-        except:
-            #self.nodesLock.release()
-            traceback.print_exc()
-            self.log(1, "core handler thread crashed")
-        self.isRunning_rx = False
-        self.log(3, "core handler thread terminated")
-    
-    #@-node:threadRxPackets
-    #@+node:threadHousekeeping
-    def threadHousekeeping(self):
-        """
-        Periodically invoke nodes' housekeeping
-        """
-        self.log(3, "\nnode housekeeping thread running")
-        try:
-            while self.isRunning:
-                #self.log(4, "calling nodes' housekeeping methods")
-                #self.nodesLock.acquire()
-                for node in self.nodes:
-                    node._doHousekeeping()
-                #self.nodesLock.release()
-                time.sleep(1)
-            self.log(3, "\nnode housekeeping thread terminated")
-        except:
-            #self.nodesLock.release()
-            traceback.print_exc()
-            self.log(1, "\nnode housekeeping thread crashed")
-    
-    #@-node:threadHousekeeping
-    #@+node:nodeWhichOwnsSock
-    def nodeWhichOwnsSock(self, sock):
-        """
-        returns ref to node which owns a socket
-        """
-        for node in self.nodes:
-            if node.sock == sock:
-                return node
-        return None
-    #@-node:nodeWhichOwnsSock
-    #@+node:cycle
-    def cycle(self):
-    
-        self.fg = True
-        self.threadRxPackets()
-    
-    #@-node:cycle
-    #@+node:run
-    def run(self, func=None):
-        """
-        Runs the core in foreground, with the client func in background
-        """
-        if func==None:
-            func = test
-    
-        self.bg = False
-    
-        thread.start_new_thread(self.runClient, (func,))
-        
-        set_trace()
-    
-        self.threadRxPackets()
-    
-    #@-node:run
-    #@+node:stop
-    def stop(self):
-        self.isRunning = False
-    
-    #@-node:stop
-    #@+node:runClient
-    def runClient(self, func):
-    
-        self.log(3, "Core: running client func")
-        try:
-            func()
-        except:
-            traceback.print_exc()
-        self.log(3, "Core: client func exited")
-        self.stop()
-    #@-node:runClient
-    #@+node:select
-    def select(self, inlist, outlist, errlist, timeout):
-        
-        return i2p.select.select(inlist, outlist, errlist, timeout)
-    
-    #@-node:select
-    #@-others
-
-#@-node:class KCore
-#@+node:create instance
-# create an instance of _KCore
-core = KCore()
-
-#@-node:create instance
-#@-node:Main Engine
-#@+node:Basic Classes
-#@+node:Node-local Storage
-#@+node:class KStorageBase
-class KStorageBase(KBase):
-    """
-    Base class for node storage objects
-
-    This needs to be overridden by implementation-specific
-    solutions.
-    """
-    #@    @+others
-    #@+node:__init__
-    def __init__(self, node, *args, **kw):
-        """
-        Override this method
-        
-        First argument should be a node instance
-        """
-        raise KNotImplemented
-    
-    #@-node:__init__
-    #@+node:putRefs
-    def putRefs(self, *refs):
-        """
-        Saves one or more noderefs
-        
-        Arguments:
-            - zero or more KPeer objects, or lists or tuples of objects
-        """
-        raise KNotImplemented
-    #@-node:putRefs
-    #@+node:getRefs
-    def getRefs(self):
-        """
-        Returns a list of KPeer objects, comprising refs
-        of peers known to this node
-        """
-        raise KNotImplemented
-    
-    #@-node:getRefs
-    #@+node:putKey
-    def putKey(self, key, value):
-        """
-        Stores value, a string, into the local storage
-        under key 'key'
-        """
-        raise KNotImplemented
-    
-    #@-node:putKey
-    #@+node:getKey
-    def getKey(self, key):
-        """
-        Attempts to retrieve item from node's local, which was
-        stored with key 'key'.
-        
-        Returns value as a string if found, or None if not present
-        """
-        raise KNotImplemented
-    #@-node:getKey
-    #@+node:private methods
-    #@+others
-    #@+node:_expandRefsList
-    def _expandRefsList(self, args, lst=None):
-        """
-        Takes a sequence of args, each of which can be a KPeer
-        object, or a list or tuple of KPeer objects, and expands
-        this into a flat list
-        """
-        if lst == None:
-            lst = []
-        for item in args:
-            if type(item) in [type(()), type([])]:
-                self._expandRefsList(item, lst)
-            else:
-                lst.append(item)
-        return lst
-    
-    #@-node:_expandRefsList
-    #@-others
-    #@-node:private methods
-    #@-others
-#@-node:class KStorageBase
-#@+node:class KStorageFile
-class KStorageFile(KStorageBase):
-    """
-    Implements node-local storage, using the local filesystem,
-    with the following hierarchy:
-        
-        - HOME ( ~ in linux, some other shit for windows)
-           - .i2pkademlia
-               - <nodename>
-                   - noderefs
-                       - <node1 base64 hash>
-                           - contains node dest, and other shit
-                       - ...
-                   - keys
-                       - <keyname1>
-                           - contains raw key value
-                       - ...
-
-    This is one ugly sukka, perhaps a db4, mysql etc implementation
-    would be better.
-    """
-    #@    @+others
-    #@+node:__init__
-    def __init__(self, node, storeDir=None):
-        """
-        Creates a persistent storage object for node
-        'nodeName', based at directory 'storeDir' (default
-        is nodeDir
-        """
-        self.node = node
-        self.nodeName = node.name
-    
-        if storeDir == None:
-            # work out local directory
-            self.topDir = userI2PDir()
-    
-        # add node dir and subdirs
-        self.nodeDir = userI2PDir(self.nodeName)
-        
-        self.refsDir = os.path.join(self.nodeDir, "noderefs")
-        if not os.path.isdir(self.refsDir):
-            os.makedirs(self.refsDir)
-    
-        self.keysDir = os.path.join(self.nodeDir, "keys")
-        if not os.path.isdir(self.keysDir):
-            os.makedirs(self.keysDir)
-    
-    #@-node:__init__
-    #@+node:putRefs
-    def putRefs(self, *args):
-        """
-        Saves one or more noderefs into filesystem
-        
-        Arguments:
-            - zero or more KPeer objects, or lists or tuples of objects
-        """
-        lst = self._expandRefsList(args)
-        for item in lst:
-            b64hash = shahash(item.dest)
-            itemPath = os.path.join(self.refsDir, b64hash)
-            itemDict = {'dest':item.dest} # might need to expand later
-            itemPickle = bencode.bencode(itemDict)
-            file(itemPath, "wb").write(itemPickle)
-        pass
-    #@-node:putRefs
-    #@+node:getRefs
-    def getRefs(self):
-        """
-        Returns a list of KPeer objects, comprising refs
-        of peers known to this node
-    
-        These are read from the directory self.refsDir.
-        Any that can't be unpickled and instantiated are dropped, but logged
-        """
-        peers = []
-        for f in os.listdir(self.refsDir):
-    
-            path = os.path.join(self.refsDir, f)
-            pickled = file(path, "rb").read()
-            try:
-                d = bencode.bdecode(pickled)
-            except:
-                self.log(3, "node %s, bad pickle ref file %s" % (
-                    self.nodeName, f))
-                continue
-            
-            # instantiate a peer object
-            try:
-                peer = KPeer(self.node, d['dest'])
-            except:
-                self.log(3, "node %s, bad unpickled ref file %s" % (
-                    self.nodeName, f))
-                continue
-    
-            # success
-            peers.append(peer)
-    
-        return peers
-    
-    #@-node:getRefs
-    #@+node:putKey
-    def putKey(self, key, val, keyIsHashed=False):
-        """
-        Stores a string into this storage under the key 'key'
-    
-        Returns True if key was saved successfully, False if not
-        """
-        try:
-            if keyIsHashed:
-                keyHashed = key
-            else:
-                keyHashed = shahash(key)
-            keyHashed = keyHashed.lower()
-            keyPath = os.path.join(self.keysDir, keyHashed)
-            file(keyPath, "wb").write(val)
-            self.log(4, "stored key: '%s'\nunder hash '%s'\n(keyIsHashed=%s)" % (
-                key, keyHashed, keyIsHashed))
-            return True
-        except:
-            traceback.print_exc()
-            self.log(3, "failed to store key")
-            return False
-    
-    #@-node:putKey
-    #@+node:getKey
-    def getKey(self, key, keyIsHashed=False):
-        """
-        Attempts to retrieve item from node's local file storage, which was
-        stored with key 'key'.
-        
-        Returns value as a string if found, or None if not present
-        """
-        try:
-            if keyIsHashed:
-                keyHashed = key
-            else:
-                keyHashed = shahash(key)
-            
-            keyHashed = keyHashed.lower()
-            self.log(4, "key=%s, keyHashed=%s, keyIsHashed=%s" % (key, keyHashed, keyIsHashed))
-    
-            keyPath = os.path.join(self.keysDir, keyHashed)
-        
-            if os.path.isfile(keyPath):
-                return file(keyPath, "rb").read()
-            else:
-                return None
-        except:
-            traceback.print_exc()
-            self.log(3, "error retrieving key '%s'" % key)
-            return None
-    
-    #@-node:getKey
-    #@-others
-#@-node:class KStorageFile
-#@-node:Node-local Storage
-#@+node:class KHash
-class KHash(KBase):
-    """
-    Wraps 160-bit hashes as abstract objects, on which
-    operations such as xor, <, >, etc can be performed.
-    
-    Kademlia node ids and keys are held as objects
-    of this class.
-
-    Internally, hashes are stored as python long ints
-    """
-    #@    @+others
-    #@+node:__init__
-    def __init__(self, val=None, **kw):
-        """
-        Create a new hash object.
-        
-        val can be one of the following:
-            - None (default) - a random value will be created
-            - long int - this will be used as the raw hash
-            - string - the string will be hashed and stored
-            - another KHash object - its value will be taken
-            - a KNode or KPeer object - its hash will be taken
-    
-        If val is not given, a raw hash value can be passed in
-        with the keyword 'raw'. Such value must be a python long int
-        or a 20-char string
-        """
-        self.value = 0L
-        if val:
-            if isinstance(val, KHash):
-                self.value = val.value
-            elif type(val) in [type(0), type(0L)]:
-                self.value = long(val)
-            elif isinstance(val, KNode) or isinstance(val, KPeer):
-                self.value = val.id.value
-            else:
-                raw = self.raw = shahash(val, bin=1)
-                for c in raw:
-                    self.value = self.value * 256 + ord(c)
-        else:
-            rawval = kw.get('raw', None)
-            if rawval == None:
-                # generate random
-                random.seed()
-                for i in range(20):
-                    self.value = self.value * 256 + random.randint(0, 256)
-            elif type(rawval) in [type(0), type(0L)]:
-                self.value = long(rawval)
-            elif type(rawval) == type(""):
-                if len(rawval) == 20:
-                    for i in rawval:
-                        self.value = self.value * 256 + ord(i)
-                elif len(rawval) == 40:
-                    try:
-                        self.value = long(rawval, 16)
-                    except:
-                        raise KBadHash(rawval)
-                else:
-                    raise KBadHash(rawval)
-            else:
-                print "rawval=%s %s %s" % (type(rawval), rawval.__class__, repr(rawval))
-                raise KBadHash(rawval)
-    
-    #@-node:__init__
-    #@+node:__str__
-    def __str__(self):
-        return "<KHash: 0x%x>" % self.value
-    
-    def __repr__(self):
-        return str(self)
-    
-    #@-node:__str__
-    #@+node:asHex
-    def asHex(self):
-        return ("%040x" % self.value).lower()
-    
-    #@-node:asHex
-    #@+node:distance
-    def distance(self, other):
-        """
-        calculates the 'distance' between this hash and another hash,
-        and returns it as i (where distance = 2^i, and 0 <= i < 160)
-        """
-    
-        #log(4, "comparing: %s\nwith %s" % (self.value, other.value))
-    
-        rawdistance = self.value ^ other.value
-        if not rawdistance:
-            return 0
-    
-        return int(math.log(rawdistance, 2))
-    
-    #@-node:distance
-    #@+node:rawdistance
-    def rawdistance(self, other):
-        """
-        calculates the 'distance' between this hash and another hash,
-        and returns it raw as this xor other
-        """
-        return self.value ^ other.value
-    
-    #@-node:rawdistance
-    #@+node:operators
-    def __eq__(self, other):
-        #log(2, "KHash: comparing %s to %s" % (self, other))
-        res = self.value == getattr(other, 'value', None)
-        #self.log(2, "KHash: res = %s" % repr(res))
-        return res
-    
-    def __ne__(self, other):
-        return not (self == other)
-    
-    def __lt__(self, other):
-        return self.value < other.value
-    
-    def __gt__(self, other):
-        return self.value > other.value
-        
-    def __le__(self, other):
-        return self.value <= other.value
-    
-    def __ge__(self, other):
-        return self.value >= other.value
-    
-    def __ne__(self, other):
-        return self.value != other.value
-    
-    def __xor__(self, other):
-        return self.value ^ other.value
-    
-    #@-node:operators
-    #@-others
-#@-node:class KHash
-#@+node:class KBucket
-class KBucket(KBase):
-    """
-    Implements the 'k-bucket' object as required in Kademlia spec
-    """
-    #@    @+others
-    #@+node:__init__
-    def __init__(self):
-        """
-        Creates a single k-bucket
-        """
-        # list of known nodes
-        # order is least recently seen at head, most recently seen at tail
-        self.nodes = []
-    
-        # list of death-row records
-        # refer spec section 2.1, paragraph 2
-        # we deviate a little:
-        #   when we hear from a new peer, and the bucket is full,
-        #   we temporarily displace the old peer, and stick the new
-        #   peer at end of list, then send out a ping
-        #   If we hear from the old peer within a reasonable time,
-        #   the new peer gets evicted and replaced with the old peer
-        #
-        # this list holds 2-tuples (oldpeer, newpeer), where
-        # oldpeer is the least-recently-seen peer that we displaced, and
-        # newpeer is the new peer we just heard from.
-        self.deathrow = []
-    
-    #@-node:__init__
-    #@+node:justSeenPeer
-    def justSeenPeer(self, peer):
-        """
-        Tells the bucket that we've just seen a given node
-        """
-        nodes = self.nodes
-    
-        if not isinstance(peer, KPeer):
-            raise KBadNode
-    
-        try:
-            idx = nodes.index(peer)
-        except:
-            idx = -1    
-        if idx >= 0:
-            del nodes[idx]
-            nodes.append(peer)
-        else:
-            nodes.append(peer)
-            
-        # might at some time need to implement death-row logic 
-        # when we set a bucket size limit - refer __init__
-    #@-node:justSeenPeer
-    #@+node:__iter__
-    def __iter__(self):
-        return iter(self.nodes)
-    #@-node:__iter__
-    #@-others
-#@-node:class KBucket
-#@+node:class KPeer
-class KPeer(KBase):
-    """
-    Encapsulates a peer node of a L{KNode},
-    storing its ID and contact info
-    """
-    #@    @+others
-    #@+node:__init__
-    def __init__(self, node, dest):
-        """
-        Create a ref to a kademlia peer node
-        
-        Arguments:
-            - node - reference to node which has the relationship
-              to this peer
-            - dest - the peer's I2P destination, as base64
-        """
-        if not isinstance(node, KNode):
-            raise KBadNode(node)
-        if not isinstance(dest, str):
-            raise KBadDest(dest)
-    
-        self.node = node
-        self.dest = dest
-        self.id = KHash(dest)
-    
-        self.justSeen()
-    
-    #@-node:__init__
-    #@+node:send_ping
-    def send_ping(self, **kw):
-        """
-        Sends a ping to remote peer
-        """
-        self.send_raw(type="ping", **kw)
-    #@-node:send_ping
-    #@+node:send_store
-    def send_store(self, **kw):
-        """
-        sends a store command to peer
-        """
-        self.log(4, "\npeer %s\ndest %s...\nsending store cmd: %s" % (self, self.dest[:12], repr(kw)))
-    
-        self.send_raw(type="store", **kw)
-    
-    #@-node:send_store
-    #@+node:send_findNode
-    def send_findNode(self, hash, **kw):
-        """
-        sends a findNode command to peer
-        """
-        if not isinstance(hash, KHash):
-            raise KBadHash
-    
-        self.log(5, "\nquerying peer %s\ntarget hash %s" % (self, hash))
-    
-        self.send_raw(type="findNode", hash=hash.value, **kw)
-    
-    #@-node:send_findNode
-    #@+node:send_findData
-    def send_findData(self, hash, **kw):
-        """
-        sends a findData command to peer
-        """
-        if not isinstance(hash, KHash):
-            raise KBadHash
-    
-        self.log(5, "\nquerying peer %s\ntarget hash %s" % (self, hash))
-    
-        self.send_raw(type="findData", hash=hash.value, **kw)
-    
-    #@-node:send_findData
-    #@+node:send_reply
-    def send_reply(self, **kw):
-        """
-        Sends an RPC reply back to upstream peer
-        """
-        self.log(5, "\nnode %s\nreplying to peer %s:\n%s" % (
-            self.node, self, kw))
-        self.send_raw(type="reply", **kw)
-    
-    #@-node:send_reply
-    #@+node:send_raw
-    def send_raw(self, **kw):
-        """
-        Sends a raw datagram to peer
-        
-        No arguments - just keywords, all of which must be strings or
-        other objects which can be bencoded
-        """
-        self.node._sendRaw(self, **kw)
-    #@-node:send_raw
-    #@+node:justSeen
-    def justSeen(self):
-        self.timeLastSeen = time.time()
-    
-    #@-node:justSeen
-    #@+node:lowlevel
-    #@+others
-    #@+node:__str__
-    def __str__(self):
-    
-        return "<KPeer:%s=>0x%s... dest %s...>" % (
-            self.node.name, ("%x" % self.id.value)[:8], self.dest[:8])
-    
-    #@-node:__str__
-    #@+node:__repr__
-    def __repr__(self):
-        
-        return str(self)
-    
-    #@-node:__repr__
-    #@+node:__eq__
-    def __eq__(self, other):
-    
-        #self.log(2, "KPeer: comparing %s to %s (%s to %s)" % (self, other, self.__class__, other.__class__))
-        res = self.id == getattr(other, 'id', None)
-        #self.log(2, "KPeer: res=%s" % res)
-        return res
-    
-    #@-node:__eq__
-    #@+node:__ne__
-    def __ne__(self, other):
-        return not (self == other)
-    #@-node:__ne__
-    #@-others
-    #@-node:lowlevel
-    #@-others
-#@-node:class KPeer
-#@-node:Basic Classes
-#@+node:RPC Classes
-#@+node:class KRpc
-class KRpc(KBase):
-    """
-    Base class for RPCs between nodes.
-    Refer subclasses
-    """
-    #@    @+others
-    #@+node:attribs
-    type = 'unknown' # override in subclass
-    
-    #@-node:attribs
-    #@+node:__init__
-    def __init__(self, localNode, client=None, **kw):
-        """
-        Holds all the information for an RPC
-    
-        Arguments:
-            - localNode - the node from which this RPC is being driven
-            - client - a representation of who is initiating this rpc, one of:
-                - None - an API caller, which is to be blocked until the RPC completes
-                  or times out
-                - (upstreamPeer, upstreamMsgId) - an upstream peer
-                - callable object - something which requires a callback upon completion
-                  in which case the callable will be invoked with the RPC results as the
-                  first argument
-    
-        Keywords:
-            - cbArgs - optional - if given, and if client is a callback, the callback
-              will be invoked with the results as first argument, and this object as
-              second argument
-        """
-        self.localNode = localNode
-    
-        if client == None:
-            # an api client
-            self.isLocal = True
-            self.queue = Queue.Queue()
-            self.callback = None
-        elif callable(client):
-            self.isLocal = False
-            self.callback = client
-        elif isinstance(client, tuple):
-            # we're doing the RPC on behalf of an upstream peer
-            upstreamPeer, upstreamMsgId = client
-            upstreamPeer = localNode._normalisePeer(upstreamPeer)
-            self.isLocal = False
-            self.upstreamPeer = upstreamPeer
-            self.upstreamMsgId = upstreamMsgId
-            self.callback = None
-    
-        # save keywords
-        self.__dict__.update(kw)
-    
-        # set time for receiving a tick.
-        # if this is set to an int absolute time value, the on_tick method
-        # will be called as soon as possible after that time
-        self.nextTickTime = None
-    
-        # and register with node as a pending command
-        self.localNode.rpcPending.append(self)
-    
-        # now start up the request
-        self.start()
-    
-    #@-node:__init__
-    #@+node:__del__
-    def __del__(self):
-    
-        #self.log(4, "\nRPC %s getting the chop" % (str(self)))
-        pass
-    
-    #@-node:__del__
-    #@+node:__str__
-    def __str__(self):
-    
-        return "<%s on node %s>" % (self.__class__.__name__, self.localNode.name)
-    
-    #@-node:__str__
-    #@+node:__repr__
-    def __repr__(self):
-        return str(self)
-    #@-node:__repr__
-    #@+node:bindPeerReply
-    def bindPeerReply(self, peer, msgId):
-        """
-        Sets up the node to give us a callback when a reply
-        comes in from downstream peer 'peer' with msg id 'msgId'
-        """
-        self.localNode.rpcBindings[(peer.dest, msgId)] = (self, peer)
-    
-    #@-node:bindPeerReply
-    #@+node:unbindPeerReply
-    def unbindPeerReply(self, peer, msgId):
-        """
-        Disables the callback from node for replies
-        from peer 'peer' with msgId 'msgId'
-        """
-        bindings = self.localNode.rpcBindings
-        peerdest = peer.dest
-        if bindings.has_key((peerdest, msgId)):
-            del bindings[(peerdest, msgId)]
-    
-    #@-node:unbindPeerReply
-    #@+node:unbindAll
-    def unbindAll(self):
-        """
-        Remove all reply bindings
-        """
-        bindings = self.localNode.rpcBindings
-        self.log(5, "node bindings before: %s" % bindings)
-        for k,v in bindings.items():
-            if v[0] == self:
-                del bindings[k]
-        self.log(5, "node bindings after: %s" % bindings)
-    
-    #@-node:unbindAll
-    #@+node:start
-    def start(self):
-        """
-        Start the RPC running.
-        Override this in subclasses
-        """
-        raise KNotImplemented
-    
-    #@-node:start
-    #@+node:execute
-    def execute(self):
-        """
-        Only for synchronous (application-level) execution.
-        Wait for the RPC to complete (or time out) and return
-        whatever it came up with
-        """
-        if core.fg:
-            print "servicing background thread"
-            while self.queue.empty():
-                core.cycle()
-    
-        return self.queue.get()
-    
-    #@-node:execute
-    #@+node:terminate
-    def terminate(self):
-        """
-        Clean up after ourselves.
-        Mainly involves removing ourself from local node
-        """
-        self.unbindAll()
-        try:
-            self.localNode.rpcPending.remove(self)
-        except:
-            #traceback.print_exc()
-            pass
-    
-    #@-node:terminate
-    #@+node:returnValue
-    def returnValue(self, res=None, **kw):
-        """
-        Passes a return value back to the original caller, be it
-        the local application, or an upstream peer
-        
-        Arguments:
-            - just one - a result object to pass back, if this RPC
-              was instigated by a local application call.
-              Note that if this RPC was instigated by an upstream
-              peer, this will be ignored.
-        
-        Keywords:
-            - the items to return, in the case that this RPC was
-              instigated by an upstream peer. Ignored if this
-              RPC was instigated by a local application call.
-              Note - the RPC invocation/reply dict keys are
-              listed at the top of this source file.
-        """
-        self.terminate()
-        if self.callback:
-            if hasattr(self, 'cbArgs'):
-                self.callback(res, self.cbArgs)
-            else:
-                self.callback(res)
-        elif self.isLocal:
-            self.queue.put(res)
-        else:
-            self.upstreamPeer.send_reply(msgId=self.upstreamMsgId,
-                                         **kw)
-    #@-node:returnValue
-    #@+node:on_reply
-    def on_reply(self, peer, msgId, **details):
-        """
-        Callback which fires when a downstream peer replies
-        
-        Override this in subclasses
-        """
-        raise KNotImplemented
-    
-    #@-node:on_reply
-    #@+node:on_tick
-    def on_tick(self):
-        """
-        Callback which fires if the whole RPC times out, in which
-        case the RPC should return whatever it can
-        
-        Override in subclasses
-        """
-        self.localNode.rpcPending.remove(self)
-    
-    #@-node:on_tick
-    #@-others
-#@-node:class KRpc
-#@+node:PING
-#@+node:class KRpcPing
-class KRpcPing(KRpc):
-    """
-    Implements the PING rpc as per Kademlia spec
-    """
-    #@    @+others
-    #@+node:attribs
-    type = 'ping'
-    
-    #@-node:attribs
-    #@+node:__init__
-    def __init__(self, localNode, client=None, **kw):
-        """
-        Creates and performs a PING RPC
-    
-        Arguments:
-            - localNode - the node performing this RPC
-            - upstreamPeer - if given, the peer wanting a reply
-            - upstreamMsgId - if upstreamPeer is given, this is the msgId
-              of the RPC message from the upstream peer
-    
-        Keywords:
-            - peer - the peer to ping - default is local node
-        """
-        peer = kw.get('peer', None)
-        if peer != None:
-            peer = localNode._normalisePeer(peer)
-        self.peerToPing = peer
-    
-        if kw.has_key('cbArgs'):
-            KRpc.__init__(self, localNode, client, cbArgs=kw['cbArgs'])
-        else:
-            KRpc.__init__(self, localNode, client)
-    
-    #@-node:__init__
-    #@+node:start
-    def start(self):
-        """
-        Sends out the ping
-        """
-        peer = self.peerToPing
-    
-        # are we ourselves being pinged?
-        if peer == None:
-            # yes, just reply
-            self.returnValue(True)
-            return
-    
-        # no - we need to ping a peer
-        thisNode = self.localNode
-    
-        msgId = thisNode.msgId = thisNode._msgIdAlloc()
-    
-        # bind for peer response
-        self.bindPeerReply(peer, msgId)
-    
-        # and send it off
-        self.log(3, "node %s sending ping" % self.localNode.name)
-        peer.send_ping(msgId=msgId)
-    
-        # and set a reply timeout
-        self.nextTickTime = time.time() + timeout['ping']
-    
-    #@-node:start
-    #@+node:on_reply
-    def on_reply(self, peer, msgId, **details):
-        """
-        Callback for PING reply
-        """
-        self.log(3, "got ping reply from %s" % peer)
-        self.returnValue(True)
-    
-    #@-node:on_reply
-    #@+node:on_tick
-    def on_tick(self):
-        """
-        'tick' handler.
-        
-        For PING RPC, the only time we should get a tick is when the ping
-        has timed out
-        """
-        self.log(3, "timeout awaiting ping reply from %s" % self.peerToPing)
-        self.returnValue(False)
-    
-    #@-node:on_tick
-    #@-others
-#@-node:class KRpcPing
-#@-node:PING
-#@+node:FIND_NODE
-#@+node:class KPeerQueryRecord
-class KPeerQueryRecord(KBase):
-    """
-    Keeps state information regarding a peer we're quering
-    """
-    #@    @+others
-    #@+node:__init__
-    def __init__(self, peer, table, state=None, **kw):
-    
-        self.peer = peer
-        self.dest = peer.dest
-        self.deadline = time.time() + timeout['findNode']
-        self.table = table
-    
-        # state is always one of:
-        #  - 'start'        - have not yet sent query to peer
-        #  - 'recommended'  - peer was recommended by another peer, no query sent
-        #  - 'queried'      - sent query, awaiting reply or timeout
-        #  - 'replied'      - this peer has replied to our query
-        #  - 'timeout'      - timed out waiting for peer reply
-        #  - 'toofar'       - too far away to be of interest
-        #  - 'closest'      - this peer is one of the closest so far
-    
-        if state == None:
-            state = 'start'
-        if not isinstance(state, str):
-            raise Exception("Invalid state %s" % state)
-    
-        self.state = state
-    
-        self.__dict__.update(kw)
-    
-    #@-node:__init__
-    #@+node:hasTimedOut
-    def hasTimedOut(self, now=None):
-        if now == None:
-            now = time.time()
-        return self.state == 'queried' and now > self.deadline
-    
-    #@-node:hasTimedOut
-    #@+node:__cmp__
-    def __cmp__(self, other):
-    
-        return cmp(self.peer.id.rawdistance(self.table.sorthash),
-                   other.peer.id.rawdistance(self.table.sorthash))
-    
-    #@-node:__cmp__
-    #@+node:__lt__ etc
-    def __lt__(self, other):
-        return (cmp(self, other) < 0)
-    
-    def __le__(self, other):
-        return (cmp(self, other) <= 0)
-    
-    def __gt__(self, other):
-        return (cmp(self, other) > 0)
-    
-    def __ge__(self, other):
-        return (cmp(self, other) <= 0)
-    
-    #@-node:__lt__ etc
-    #@+node:isCloserThanAllOf
-    def isCloserThanAllOf(self, tab):
-        """
-        returns True if this peerRec is closer to the desired hash
-        than all of the peerRecs in table 'tab'
-        """
-        if not isinstance(tab, KPeerQueryTable):
-            self.log(2, "invalid qtable %s" % repr(tab))
-            raise Exception("invalid qtable %s" % repr(tab))
-        
-        for rec in tab:
-            if self > rec:
-                return False
-        return True
-    
-    #@-node:isCloserThanAllOf
-    #@+node:isCloserThanOneOf
-    def isCloserThanOneOf(self, tab):
-        """
-        returns True if this peerRec is closer to the desired hash
-        than one or more of of the peerRecs in table 'tab'
-        """
-        if not isinstance(tab, KPeerQueryTable):
-            self.log(2, "invalid qtable %s" % repr(tab))
-            raise Exception("invalid qtable %s" % repr(tab))
-        
-        for rec in tab:
-            if self < rec:
-                return True
-        return False
-    
-    #@-node:isCloserThanOneOf
-    #@-others
-#@-node:class KPeerQueryRecord
-#@+node:class KPeerQueryTable
-class KPeerQueryTable(KBase):
-    """
-    Holds zero or more instances of KPeerQuery and
-    presents/sorts table in different forms
-    """
-    #@    @+others
-    #@+node:__init__
-    def __init__(self, lst=None, sorthash=None, state=None, **kw):
-        self.peers = []
-        if lst == None:
-            lst = []
-        else:
-            self.setlist(lst, state, **kw)
-        self.sorthash = sorthash
-    
-    #@-node:__init__
-    #@+node:setlist
-    def setlist(self, lst, state=None, **kw):
-        for item in lst:
-            self.append(item, state, **kw)
-    
-    #@-node:setlist
-    #@+node:getExpired
-    def getExpired(self):
-        """
-        return a list of peers which have expired
-        """
-        return KPeerQueryTable(
-                filter(lambda item: item.hasTimedOut(), self.peers),
-                self.sorthash
-                )
-    
-    #@-node:getExpired
-    #@+node:purgeExpired
-    def purgeExpired(self):
-        """
-        Eliminate peers which have expired
-        """
-        for peer in self.peers:
-            if peer.hasTimedOut():
-                self.peers.remove(peer)
-    
-    #@-node:purgeExpired
-    #@+node:sort
-    def sort(self):
-        """
-        Sort the table in order of increasing distance from self.sorthash
-        """
-        self.peers.sort()
-    
-    #@-node:sort
-    #@+node:select
-    def select(self, criterion):
-        """
-        Returns a table of items for which criterion(item) returns True
-        Otherwise, if 'criterion' is a string, returns the items whose
-        state == criterion.
-        Otherwise, if 'criterion' is a list or tuple, return the items
-        whose state is one of the elements in criterion
-        """
-        if callable(criterion):
-            func = criterion
-        elif type(criterion) in [type(()), type([])]:
-            func = lambda p: p.state in criterion
-        else:
-            func = lambda p: p.state == criterion
-    
-        recs = []
-        for peerRec in self.peers:
-            if func(peerRec):
-                recs.append(peerRec)
-        return self.newtable(recs)
-    
-    #@-node:select
-    #@+node:count
-    def count(self, *args):
-        """
-        returns the number of records whose state is one of args
-        """
-        count = 0
-        for rec in self.peers:
-            if rec.state in args:
-                count += 1
-        return count
-    
-    #@-node:count
-    #@+node:changeState
-    def changeState(self, oldstate, newstate):
-        """
-        for all recs of state 'oldstate', change their
-        state to 'newstate'
-        """
-        for p in self.peers:
-            if p.state == oldstate:
-                p.state = newstate
-    #@-node:changeState
-    #@+node:filter
-    def filter(self, func):
-        """
-        Eliminate, in place, all items where func(item) returns False
-        """
-        for peerRec in self.peers:
-            if not func(peerRec):
-                self.peers.remove(peerRec)
-    
-    #@-node:filter
-    #@+node:purge
-    def purge(self, func):
-        """
-        Eliminate, in place, all items where func(item) returns True
-        """
-        if 0 and desperatelyDebugging:
-            set_trace()
-        for peerRec in self.peers:
-            if func(peerRec):
-                self.peers.remove(peerRec)
-    
-    #@-node:purge
-    #@+node:chooseN
-    def chooseN(self, n):
-        """
-        Randomly select n peer query records
-        """
-        candidates = self.peers[:]
-    
-        self.log(3, "candidates = %s" % repr(candidates))
-    
-        chosen = []
-        i = 0
-    
-        if len(candidates) <= n:
-            chosen = candidates
-        else:
-            while i < n:
-                try:
-                    peer = random.choice(candidates)
-                except:
-                    self.log(2, "failed to choose one of %s" % repr(candidates))
-                    raise
-                chosen.append(peer)
-                candidates.remove(peer)
-                i += 1
-    
-        return self.newtable(chosen)
-    
-    #@-node:chooseN
-    #@+node:__str__
-    def __str__(self):
-        return "<KPeerQueryTable: %d peers>" % len(self) #.peers)
-    
-    def __repr__(self):
-        return str(self)
-    
-    #@-node:__str__
-    #@+node:newtable
-    def newtable(self, items, state=None, **kw):
-        """
-        Returns a new KPeerQueryTable object, based on this
-        one, but containing 'items'
-        """
-        tab = KPeerQueryTable(items, sorthash=self.sorthash, state=state, **kw)
-        return tab
-    
-    #@-node:newtable
-    #@+node:dump
-    def dump(self):
-    
-        c = self.count
-        self.log(2,
-                "PeerQueryTable stats:\n"
-                "start:       %s\n"
-                "recommended: %s\n"
-                "queried:     %s\n"
-                "replied:     %s\n"
-                "timeout:     %s\n"
-                "closest:     %s\n"
-                "toofar:      %s\n"
-                "TOTAL:       %s\n" % (
-                    c('start'),
-                    c('recommended'),
-                    c('queried'),
-                    c('replied'),
-                    c('timeout'),
-                    c('closest'),
-                    c('toofar'),
-                    len(self.peers)))
-                    
-        #states = [p.state for p in self.peers]
-        #self.log(3, "PeerQueryTable states:\n%s" % states)
-    
-    #@-node:dump
-    #@+node:list-like methods
-    #@+node:extend
-    def extend(self, items, state, **kw):
-        for item in items:
-            self.append(item, state, **kw)
-    
-    #@-node:extend
-    #@+node:append
-    def append(self, item, state=None, **kw):
-    
-        if isinstance(item, KPeerQueryRecord):
-            self.log(5, "adding a KPeerQueryRecord, state=%s" % state)
-            if state != None:
-                item.state = state
-            item.__dict__.update(kw)
-            peerRec = item
-    
-        elif isinstance(item, KPeer):
-            self.log(5, "adding a KPeer")
-            peerRec = KPeerQueryRecord(item, self, state, **kw)
-    
-        else:
-            self.log(2, "bad peer %s" % repr(item))
-            raise KBadPeer
-    
-        if peerRec not in self:
-            self.log(5, "peerRec=%s list=%s" % (peerRec, self.peers))
-            self.peers.append(peerRec)
-        else:
-            self.log(5, "trying to append duplicate peer???")
-    
-    #@-node:append
-    #@+node:remove
-    def remove(self, item):
-        self.peers.remove(item)
-    
-    #@-node:remove
-    #@+node:__getitem__
-    def __getitem__(self, idx):
-        """
-        Allow the table to be indexed by any of:
-            - KPeerQueryRecord
-            - integer index
-            - long string - treated as dest
-            - short string - treated as peer id hash string
-            - KHash - finds peer with that id
-            - KPeer - returns peer with that peer
-        """
-        if type(idx) == type(0):
-            return self.peers[idx]
-        elif isinstance(idx, KPeer):
-            for peer in self.peers:
-                if peer.peer == idx:
-                    return peer
-            raise IndexError("Query table has no peer %s" % idx)
-        elif isinstance(idx, str):
-            if len(str) > 512:
-                for peer in self.peers:
-                    if peer.peer.dest == idx:
-                        return peer
-                raise IndexError("No peer with dest %s" % idx)
-            else:
-                for peer in self.peers:
-                    if peer.peer.id.value == idx:
-                        return peer
-                raise IndexError("No peer with dest hash %s" % idx)
-        elif isinstance(idx, KHash):
-            for peer in self.peers:
-                if peer.peer.id == idx:
-                    return peer
-                raise IndexError("No peer with id %s" % idx)
-        else:
-            raise IndexError("Invalid selector %s" % repr(idx))
-    
-    #@-node:__getitem__
-    #@+node:__len__
-    def __len__(self):
-        return len(self.peers)
-    
-    #@-node:__len__
-    #@+node:__getslice__
-    def __getslice__(self, fromidx, toidx):
-        return KPeerQueryTable(self.peers[fromidx:toidx], self.sorthash)
-    
-    #@-node:__getslice__
-    #@+node:__iter__
-    def __iter__(self):
-        return iter(self.peers)
-    
-    #@-node:__iter__
-    #@+node:__add__
-    def __add__(self, other):
-        self.extend(other)
-    
-    #@-node:__add__
-    #@+node:__contains__
-    def __contains__(self, other):
-        self.log(5, "testing if %s is in %s" % (other, self.peers))
-        for peerRec in self.peers:
-            if peerRec.peer.dest == other.peer.dest:
-                return True
-        return False
-    
-    #@-node:__contains__
-    #@-node:list-like methods
-    #@-others
-#@-node:class KPeerQueryTable
-#@+node:class KRpcFindNode
-class KRpcFindNode(KRpc):
-    """
-    Implements the FIND_NODE rpc as per Kademlia spec
-    """
-    #@    @+others
-    #@+node:spec info comments
-    #@+at
-    # Verbatim extract from original Kademlia paper follows:
-    # 
-    # The lookup initiator starts by picking x nodes from its closest
-    # non-empty k-bucket (or, if that bucket has fewer than x
-    # entries, it just takes the closest x nodes it knows of).
-    # 
-    # The initiator then sends parallel, asynchronous
-    # FIND NODE RPCs to the x nodes it has chosen.
-    # x is a system-wide concurrency parameter, such as 3.
-    # 
-    # In the recursive step, the initiator resends the
-    # FIND NODE to nodes it has learned about from previous RPCs.
-    # 
-    # [Paraphrased - in the recursive step, the initiator sends a FIND_NODE to
-    # each of the nodes that were returned as results of these previous
-    # FIND_NODE RPCs.]
-    # 
-    # (This recursion can begin before all of the previous RPCs have
-    # returned).
-    # 
-    # Of the k nodes the initiator has heard of closest to
-    # the target, it picks x that it has not yet queried and resends
-    # the FIND_NODE RPC to them.
-    # 
-    # Nodes that fail to respond quickly are removed from consideration
-    # until and unless they do respond.
-    # 
-    # If a round of FIND_NODEs fails to return a node any closer
-    # than the closest already seen, the initiator resends
-    # the FIND NODE to all of the k closest nodes it has
-    # not already queried.
-    # 
-    # The lookup terminates when the initiator has queried and gotten
-    # responses from the k closest nodes it has seen.
-    #@-at
-    #@-node:spec info comments
-    #@+node:attribs
-    type = 'findNode'
-    #@-node:attribs
-    #@+node:__init__
-    def __init__(self, localNode, client=None, **kw):
-        """
-        Creates and launches the findNode rpc
-        
-        Arguments:
-            - localNode - the node performing this RPC
-            - client - see KRpc.__init__
-    
-        Keywords:
-            - hash - a string, long int or KHash object representing
-              what we're looking for. treatment depends on type:
-                  - KHash object - used as is
-                  - string - gets wrapped into a KHash object
-                  - long int - wrapped into a KHash object
-              refer KHash.__init__
-            - raw - whether 'hash' is already a hash, default True
-            - local - True/False - whether to only search local store,
-              or pass on the query to the network, default True
-        """
-        kw = dict(kw)
-        if kw.get('raw', False):
-            h = kw['hash']
-            del kw['hash']
-            kw['raw'] = h
-            self.hashWanted = KHash(**kw)
-        else:
-            self.hashWanted = KHash(kw['hash'], **kw)
-        self.isLocalOnly = kw.get('local', True)
-    
-        self.numQueriesPending = 0
-    
-        self.numRounds = 0   # count number of rounds
-        self.numReplies = 0  # number of query replies received
-        self.numQueriesSent = 0
-        self.numPeersRecommended = 0
-    
-        # whichever mode we're called from, we gotta find the k closest peers
-        self.localNode = localNode
-        self.peerTab = self.findClosestPeersInitial()
-    
-        self.log(4, "KRpcFindNode: isLocalOnly=%s" % self.isLocalOnly)
-    
-        if kw.has_key('cbArgs'):
-            KRpc.__init__(self, localNode, client, cbArgs=kw['cbArgs'])
-        else:
-            KRpc.__init__(self, localNode, client)
-    
-    #@-node:__init__
-    #@+node:start
-    def start(self):
-        """
-        Kicks off this RPC
-        """
-        # if we're being called by an upstream initiator, just return the peer list
-        if self.isLocalOnly:
-            peerDests = [peer.dest for peer in self.peerTab]
-            self.log(5, "findNode: local only: returning to upstream with %s" % repr(peerDests))
-            self.returnValue(peerDests)
-            return
-    
-        # just return nothing if we don't have any peers
-        if len(self.peerTab) == 0:
-            self.returnValue([])
-            return
-    
-        # send off first round of queries
-        self.sendSomeQueries()
-    
-        return
-    
-    #@-node:start
-    #@+node:sendSomeQueries
-    def sendSomeQueries(self, **kw):
-        """
-        First step of findNode
-        
-        Select alpha nodes that we haven't yet queried, and send them queries
-        """
-        # bail if too busy
-        if self.numQueriesPending >= maxConcurrentQueries:
-            return
-    
-        # shorthand
-        localNode = self.localNode
-        hashWanted = self.hashWanted
-    
-        # randomly choose some peers
-        #somePeerRecs = self.peerTab.chooseN(numSearchPeers)
-        somePeerRecs = self.peerTab.select('start')
-        
-        # start our ticker
-        self.nextTickTime = time.time() + timeout['findNode']
-    
-        numQueriesSent = 0
-    
-        # and send them findNode queries
-        if len(somePeerRecs) > 0:
-            for peerRec in somePeerRecs:
-                self.log(3, "querying %s" % peerRec)
-                if self.numQueriesPending < maxConcurrentQueries:
-                    self.sendOneQuery(peerRec)
-                    numQueriesSent += 1
-                else:
-                    break
-            self.log(3, "%s queries sent, awaiting reply" % numQueriesSent)
-        else:
-            self.log(3, "no peer recs???")
-            for peerRec in self.peerTab:
-                self.log(4, "%s state=%s, dest=%s..." % (peerRec, peerRec.state, peerRec.dest[:12]))
-    
-    #@-node:sendSomeQueries
-    #@+node:sendOneQuery
-    def sendOneQuery(self, peerRec):
-        """
-        Sends off a query to a single peer
-        """
-        if peerRec.state != 'start':
-            self.log(2, "duh!! peer state %s:\n%s" % (peerRec.state, peerRec))
-            return 
-    
-        msgId = self.localNode._msgIdAlloc()
-        self.bindPeerReply(peerRec.peer, msgId)
-        peerRec.msgId = msgId
-    
-        if self.type == 'findData':
-            peerRec.peer.send_findData(hash=self.hashWanted, msgId=msgId)
-        else:
-            peerRec.peer.send_findNode(hash=self.hashWanted, msgId=msgId)
-    
-        peerRec.state = 'queried'
-    
-        self.numQueriesPending += 1
-    
-        self.numQueriesSent += 1
-    
-    #@-node:sendOneQuery
-    #@+node:findClosestPeersInitial
-    def findClosestPeersInitial(self):
-        """
-        Searches our k-buckets, and returns a table of k of
-        peers closest to wanted hash into self.closestPeersInitial
-        """
-        hashobj = self.hashWanted
-    
-        lst = []
-        buckets = self.localNode.buckets
-        for bucket in buckets:
-            for peer in bucket:
-                lst.append(peer)
-    
-        table = KPeerQueryTable(lst, self.hashWanted, 'start')
-        table.sort()
-    
-        return table[:maxBucketSize]
-    
-    #@-node:findClosestPeersInitial
-    #@+node:addPeerIfCloser
-    def addPeerIfCloser(self, peer):
-        """
-        Maintains the private .peersToQuery array.
-        If the array is not yet maxed (ie, length < maxBucketSize),
-        the peer is simply added.
-        However, if the array is maxed, it finds the least-close peer,
-        and replaces it with the given peer if closer.
-        """
-    #@-node:addPeerIfCloser
-    #@+node:isCloserThanQueried
-    def isCloserThanQueried(self, peer):
-        """
-        Test function which returns True if argument 'peer'
-        is closer than all the peers in self.peersAlreadyQueried,
-        or False if not
-        """
-        for p in self.peersAlreadyQueried:
-            if p.id.rawdistance(self.hashWanted) < peer.id.rawdistance(self.hashWanted):
-                return False
-        return True
-    
-    #@-node:isCloserThanQueried
-    #@+node:on_reply
-    def on_reply(self, peer, msgId, **details):
-        """
-        Callback for FIND_NODE reply
-        """
-        # shorthand
-        peerTab = self.peerTab
-    
-        self.numReplies += 1
-    
-        # ------------------------------------------------------------
-        # determine who replied, and get the raw dests sent back
-        try:
-            peerRec = peerTab[peer]
-        except:
-            traceback.print_exc()
-            self.log(3, "discarding findNode reply from unknown peer %s %s, discarding" % (
-                peer, details))
-            return
-    
-        # one less query to wait for
-        self.numQueriesPending -= 1
-    
-        # ----------------------------------------------------------
-        # peerRec is the peer that replied
-        # peers is a list of raw dests
-    
-        # save ref to this peer, it's seemingly good
-        self.localNode.addref(peerRec.peer)
-    
-        # mark it as having replied
-        if peerRec.state != 'queried':
-            self.log(2, "too weird - got a reply from a peer we didn't query")
-        peerRec.state = 'replied'
-    
-        # wrap the returned peers as KPeer objects
-        peersReturned = details.get('result', [])
-        peersReturned = [self.localNode._normalisePeer(p) for p in peersReturned]
-    
-        self.numPeersRecommended += len(peersReturned)
-    
-        # and add them to table in state 'recommended'
-        for p in peersReturned:
-            peerTab.append(p, 'recommended')
-    
-        # try to fire off more queries
-        self.sendSomeQueries()
-    
-        # and check for and action possible end of query round
-        self.checkEndOfRound()
-    
-    
-    #@-node:on_reply
-    #@+node:on_tick
-    def on_tick(self):
-        """
-        Callback for FIND_NODE reply timeout
-        """
-        # check for timeouts, and update offending peers
-        now = time.time()
-        for peerRec in self.peerTab:
-            if peerRec.hasTimedOut(now):
-                peerRec.state = 'timeout'
-    
-                # makes room for more queries
-                self.sendSomeQueries()
-    
-        # possible end of round
-        self.checkEndOfRound()
-    
-        # schedule next tick
-        self.nextTickTime = time.time() + 5
-    
-    #@-node:on_tick
-    #@+node:checkEndOfRound
-    def checkEndOfRound(self):
-        """
-        Checks if we've hit the end of a query round.
-        If so, and if either:
-            - we've got some closer peers, OR
-            - we've heard from less than maxBucketSize peers,
-        fire off more queries
-        
-        Otherwise, return the best available
-        """
-        peerTab = self.peerTab
-    
-        if core.fg:
-            set_trace()
-    
-        # has this query round ended?
-        if peerTab.count('start', 'queried') > 0:
-            # not yet
-            return
-    
-        self.log(2, "********** query round ended")
-    
-        # ------------------------------------
-        # end of round processing
-    
-        self.numRounds += 1
-    
-        # did we get any closer to required hash?
-        if self.type == 'findData' \
-                or self.gotAnyCloser() \
-                or peerTab.count('closest') < maxBucketSize:
-    
-            # yes - save these query results
-            self.log(4, "starting another round")
-            peerTab.changeState('replied', 'closest')
-            peerTab.changeState('recommended', 'start')
-    
-            # cull the shortlist
-            self.log(2, "culling to k peers")
-            if peerTab.count('closest') > maxBucketSize:
-                peerTab.sort()
-                excess = peerTab.select('closest')[maxBucketSize:]
-                excess.changeState('closest', 'toofar')
-                pass
-    
-            # and start up another round
-            self.sendSomeQueries()
-    
-        # did anything launch?
-        if peerTab.count('start', 'queried') == 0:
-            # no - we're screwed
-            self.returnTheBestWeGot()
-            
-        # done for now
-        return
-    
-    #@-node:checkEndOfRound
-    #@+node:gotAnyCloser
-    def gotAnyCloser(self):
-        """
-        Tests if any peer records in state 'recommended' or 'replied'
-        are nearer than the records in state 'closest'
-        """
-        peerTab = self.peerTab
-    
-        # get current closest peers
-        closest = peerTab.select('closest')
-    
-        # if none yet, then this was just end of first round
-        if len(closest) == 0:
-            return True
-    
-        # get the peers we're considering
-        #candidates = peerTab.select(('recommended', 'replied'))
-        candidates = peerTab.select('recommended')
-    
-        # now test them
-        gotOneCloser = False
-        for c in candidates:
-            #if c.isCloserThanOneOf(closest):
-            if c.isCloserThanAllOf(closest):
-                return True
-    
-        # none were closer
-        return False
-    
-    #@-node:gotAnyCloser
-    #@+node:returnTheBestWeGot
-    def returnTheBestWeGot(self):
-        """
-        Returns the k closest nodes to the wanted hash that we have
-        actually heard from
-        """
-        # pick the peers which have replied to us
-        closest = self.peerTab.select('closest')
-        
-        self.peerTab.dump()
-    
-        # add ourself to the list - we could easily be one of the best
-        localNode = self.localNode
-        selfDest = localNode._normalisePeer(localNode.dest) 
-        closest.append(selfDest, state='closest')
-        
-        # sort in order of least distance first
-        closest.sort()
-    
-        # pick the best k of these
-        #peersHeardFrom = peersHeardFrom[:maxBucketSize]
-        #peersHeardFrom = peersHeardFrom[:numSearchPeers]
-    
-        # extract their dest strings
-        peers = [p.peer.dest for p in closest]
-    
-        # pass these back
-        self.returnValue(peers)
-    
-        # and we're done
-        return
-    
-    #@-node:returnTheBestWeGot
-    #@+node:returnValue
-    def returnValue(self, items):
-        """
-        override with a nicer call sig
-        """
-        # a hack for testing - save this RPC object into the node
-        # so we can introspect it
-        self.localNode.lastrpc = self
-    
-        items = items[:maxBucketSize]
-    
-        self.reportStats()
-    
-        KRpc.returnValue(self, items, result=items)
-    
-    
-    #@-node:returnValue
-    #@+node:reportStats
-    def reportStats(self):
-        """
-        Logs a stat dump of query outcome
-        """
-        if self.isLocalOnly:
-            return
-        self.log(2,
-            "query terminated after %s rounds, %s queries, %s replies, %s recommendations" % (
-               (self.numRounds+1),
-               self.numQueriesSent,
-               (self.numReplies+1),
-               self.numPeersRecommended
-               )
-            )
-    #@-node:reportStats
-    #@-others
-#@-node:class KRpcFindNode
-#@-node:FIND_NODE
-#@+node:FIND_DATA
-#@+node:class KRpcFindData
-class KRpcFindData(KRpcFindNode):
-    """
-    variant of KRpcFindNode which returns key value if found
-    """
-    #@    @+others
-    #@+node:attribs
-    type = 'findData'
-    #@-node:attribs
-    #@+node:start
-    def start(self):
-        """
-        Kicks off the RPC.
-        If requested key is stored locally, simply returns it.
-        Otherwise, falls back on parent method
-        """
-        # if we posses the data, just return the data
-        value = self.localNode.storage.getKey(self.hashWanted.asHex(), keyIsHashed=True)
-        if value != None:
-            self.log(4, "Found required value in local storage")
-            self.log(4, "VALUE='%s'" % value)
-            self.on_gotValue(value, self.hashWanted.asHex())
-            return
-    
-        # no such luck - pass on to parent
-        KRpcFindNode.start(self)
-    
-    #@-node:start
-    #@+node:on_reply
-    def on_reply(self, peer, msgId, **details):
-        """
-        Callback for FIND_NODE reply
-        """
-        res = details.get('result', None)
-        if isinstance(res, str):
-            self.on_gotValue(res, self.hashWanted.asHex())
-        else:
-            KRpcFindNode.on_reply(self, peer, msgId, **details)
-    
-    #@-node:on_reply
-    #@+node:on_gotValue
-    def on_gotValue(self, value, hash=None):
-        """
-        Callback which fires when we get the value stored under a key
-        
-        Value is either the real value, or a splitfile manifest
-        If a real value, just return it.
-        If a splitfile manifest, launch nested findValue RPCs to get each chunk
-        """
-        nchunks = 0
-        try:
-            firstline, rest = value.split("\n", 1)
-            firstline = firstline.strip()
-            kwd, str_nchunks = firstline.split(":")
-            if kwd != 'chunks':
-                raise hell
-            nchunks = int(nchunks)
-            value = rest
-        except:
-            pass # in this case, hell hath no fury at all
-        
-        if nchunks == 0:
-            self.returnValue(value)
-            return
-    
-        # now we get to the hard bit - we have to set up nested findData RPCs to
-        # get all the chunks and reassemble them
-        hashes = rest.strip().split("\n")
-        
-        # do sanity checks
-        hashesAllValid = [len(h) == 40 for h in hashes]
-        if len(hashes) != nchunks:
-            self.log(
-                2,
-                "Splitfile retrieval failure\nmanifest contains %s hashes, should have been %s" % (
-                    len(hashes), nchunks))
-            self.returnValue(None)
-        if False in hashesAllValid:
-            self.log(2, "Splitfile retrieval failure - one or more invalid hashes")
-    
-        # now this is a bit weird - we need to bind each chunk to its hash, so we create a
-        # class which produces callables which fire our on_gotChunk callback
-        class ChunkNotifier:
-            def __init__(me, h, cb):
-                me.h = h
-                me.cb = cb
-            def __call__(me, val):
-                me.cb(me.h, val)
-        
-        # now launch the chunk retrieval RPCs
-        # result is that for each retrieved chunk, our on_gotChunk callback will
-        # be invoked with the arguments (hash, value), so we can tick them off
-        self.numChunks = nchunks
-        self.numChunksReceived = 0
-        self.chunkHashes = hashes
-        self.chunks = dict.fromkeys(hashes)
-        for h in hashes:
-            KRpcFindData(self.localNode, h, ChunkNotifier(h, self.on_gotChunk))
-    
-        # now, we can sit back and receive the chunks
-    
-    #@-node:on_gotValue
-    #@+node:on_gotChunk
-    def on_gotChunk(self, hexhash, value):
-        """
-        Callback which fires when a nested chunk findNode returns
-        """
-        if value == None:
-            self.log(2, "Chunk retrieval failed, fatal to this findData")
-            self.returnValue(None)
-            return
-        
-        # got a value - vet it against hash
-        if shahash(value) != hexhash:
-            self.log(2, "Got a chunk, but it doesn't hash right - fatal to this findData")
-            self.returnValue(None)
-            return
-    
-        # it's valid - stash it
-        self.chunks[hexhash] = value
-        self.numChunksReceived += 1
-    
-        # have we finished yet?
-        if self.numChunksReceived <= self.numChunks:
-            # no
-            self.log(4, "Received chunk %s of %s" % (self.numChunksReceived, self.numChunks))
-            return
-    
-        # maybe we have
-        self.log(4, "We appear to have all chunks, checking further")
-    
-        # sanity check
-        if None in self.chunks.values():
-            self.log(2, "Fatal - reached chunk count, but chunks still missing")
-            self.returnValue(None)
-            return
-                
-        # finally done - got all chunks, hashes are valid, reassemble in order
-        allChunks = [self.chunks[h] for h in self.chunkHashes]
-        reassembled = "".join(allChunks)
-        self.log(4, "Reassembled all %s chunks, SUCCESS" % self.numChunks)
-        self.returnValue(reassembled)
-    
-    #@-node:on_gotChunk
-    #@+node:returnValue
-    def returnValue(self, items):
-        """
-        override with a nicer call sig
-        """
-        # a hack for testing - save this RPC object into the node
-        # so we can introspect it
-        self.localNode.lastrpc = self
-    
-        # another debugging hack
-        self.reportStats()
-    
-        KRpc.returnValue(self, items, result=items)
-    
-    #@-node:returnValue
-    #@-others
-
-#@-node:class KRpcFindData
-#@-node:FIND_DATA
-#@+node:STORE
-#@+node:class KRpcStore
-class KRpcStore(KRpc):
-    """
-    Implements key storage
-    """
-    #@    @+others
-    #@+node:attribs
-    type = 'store'
-    #@-node:attribs
-    #@+node:__init__
-    def __init__(self, localNode, client=None, **kw):
-        """
-        Creates and launches a STORE rpc
-        
-        Arguments:
-            - localNode - the node performing this RPC
-            - client - see KRpc.__init__
-    
-        Keywords:
-            - key - the key under which we wish to save the data
-            - value - the value we wish to save
-            - local - True/False:
-                - if True, only save in local store
-                - if False, do a findNode to find the nodes to save the
-                  key to, and tell them to save it
-              default is True
-        """
-        self.key = kw['key']
-        #self.keyHashed = shahash(self.key)
-        self.keyHashed = self.key
-        self.value = kw['value']
-        self.isLocalOnly = kw.get('local', True)
-    
-        # set 'splitting' flag to indicate if we need to insert as splitfiles
-        self.splitting = len(self.value) > maxValueSize
-    
-        self.log(4, "isLocalOnly=%s" % self.isLocalOnly)
-    
-        if kw.has_key('cbArgs'):
-            KRpc.__init__(self, localNode, client, cbArgs=kw['cbArgs'])
-        else:
-            KRpc.__init__(self, localNode, client)
-    
-    #@-node:__init__
-    #@+node:start
-    def start(self):
-        """
-        Kicks off this RPC
-        """
-        # if too big, then break up into <30k chunks
-        if self.splitting:
-            self.storeSplit()
-            return
-    
-        # not too big - prefix a 0 chunk count, and go ahead as a single entity
-        self.value = "chunks:0\n" + self.value
-            
-        # if local only, or no peers, just save locally
-        if self.isLocalOnly or len(self.localNode.peers) == 0:
-            result = self.localNode.storage.putKey(self.keyHashed, self.value, keyIsHashed=True)
-            if result:
-                result = 1
-            else:
-                result = 0
-            self.returnValue(result)
-            return
-    
-        # no - se have to find peers to store the key to, and tell them to
-        # store the key
-        
-        # launch a findNode rpc, continue in our callback
-        KRpcFindNode(self.localNode, self.on_doneFindNode,
-                     hash=self.keyHashed, raw=True, local=False)
-        return
-    
-    
-    #@-node:start
-    #@+node:storeSplit
-    def storeSplit(self):
-        """
-        Gets called if we're splitting a big file into smaller chunks
-        
-        Here, we:
-            - break the file up into chunks
-            - build a manifest
-            - launch store RPCs to store each chunk, where the key is SHA(chunk)
-            - launch a store RPC to store the 'manifest' (noting that if the manifest
-              is too big, it'll get recursively inserted as a splitfile as well
-        """
-        # break up into chunks
-        chunks = []
-        hashes = []
-        size = len(self.value)
-        i = 0
-        self.nchunks = 0
-        while i < size:
-            chunks.append(self.value[i:i+maxValueSize])
-            hashes.append(shahash(chunks[-1]))
-            i += maxValueSize
-            self.nchunks += 1
-    
-        # build the manifest
-        manifest = "chunks:%s\n%s\n" % (self.nchunks, "\n".join(hashes))
-        
-        # set progress attributes
-        self.chunkManifestInserted = False
-        self.chunksInserted = 0
-    
-        # launch nested Store RPCs for manifest, and each chunk
-        KRpcStore(self.localNode, self.on_doneChunkManifest,
-                  local=self.isLocalOnly,
-                  key=self.key,
-                  value=manifest)
-        i = 0
-        while i < self.nchunks:
-            KRpcStore(self.localNode, self.on_doneChunk,
-                      local=self.isLocalOnly,
-                      key=hashes[i],
-                      value=chunks[i])
-            i += 1
-    
-        # now sit back and wait for the callbacks
-    #@-node:storeSplit
-    #@+node:on_doneChunkManifest
-    def on_doneChunkManifest(self, result):
-        """
-        Callback which fires when a manifest insert succeeds/fails
-        """
-        # the chunk callback handles all
-        self.on_doneChunk(result, isManifest=True)
-    #@-node:on_doneChunkManifest
-    #@+node:on_doneChunk
-    def on_doneChunk(self, result, isManifest=False):
-        """
-        Callback which fires when a single chunk insert succeeds/fails
-        """
-        # a failure either way means the whole RPC has failed
-        if not result:
-            # one huge fuck-up
-            self.returnValue(False)
-            return
-    
-        # update our tally
-        if isManifest:
-            self.chunkManifestInserted = True
-        else:
-            self.chunksInserted += 1
-    
-        # finished?
-        if self.chunkManifestInserted and (self.chunksInserted == self.nchunks):
-            # yep = success
-            self.returnValue(True)
-    
-    #@-node:on_doneChunk
-    #@+node:returnValue
-    def returnValue(self, result):
-        """
-        an override with a nicer call sig
-        """
-        # a hack for testing - save this RPC object into the node
-        # so we can introspect it
-        self.localNode.lastrpc = self
-    
-        try:
-            KRpc.returnValue(self, result, status=result)
-        except:
-            traceback.print_exc()
-            self.log(3, "Failed to return %s" % repr(result))
-            KRpc.returnValue(self, 0, status=0)
-    
-    #@-node:returnValue
-    #@+node:on_doneFindNode
-    def on_doneFindNode(self, lst):
-        """
-        Receive a callback from findNode
-        
-        Send STORE command to each node that comes back
-        """
-        localNode = self.localNode
-    
-        # normalise results
-        normalisePeer = localNode._normalisePeer
-        peers = [normalisePeer(p) for p in lst] # wrap in KPeer objects
-    
-        self.log(2, "STORE RPC findNode - got peers %s" % repr(peers))
-    
-        i = 0
-    
-        self.numPeersSucceeded = 0
-        self.numPeersFailed = 0
-        self.numPeersFinished = 0
-    
-        # and fire off store messages for each peer
-        for peer in peers:
-    
-            if peer.dest == localNode.dest:
-                self.log(3, "storing to ourself")
-                localNode.storage.putKey(self.keyHashed, self.value, keyIsHashed=True)
-                self.numPeersSucceeded += 1
-                self.numPeersFinished += 1
-            else:
-                msgId = self.localNode._msgIdAlloc()
-                self.log(4, "forwarding store cmd to peer:\npeer=%s\nmsgId=%s" % (peer, msgId))
-                self.bindPeerReply(peer, msgId)
-                peer.send_store(key=self.keyHashed, value=self.value, msgId=msgId)
-            i += 1
-            if i >= numStorePeers:
-                break
-    
-        self.nextTickTime = time.time() + timeout['store']
-    
-        self.log(2, "Sent store cmd to %s peers, awaiting responses" % i)
-    
-        self.numPeersToStore = i
-    
-    
-    #@-node:on_doneFindNode
-    #@+node:on_reply
-    def on_reply(self, peer, msgId, **details):
-        """
-        callback which fires when we get a reply from a STORE we sent to a
-        peer
-        """
-        self.numPeersSucceeded += 1
-        self.numPeersFinished += 1
-        
-        if self.numPeersFinished == self.numPeersToStore:
-            # rpc is finished
-            self.returnValue(True)
-    
-    #@-node:on_reply
-    #@+node:on_tick
-    def on_tick(self):
-    
-        self.log(3, "Timeout awaiting store reply from %d out of %d peers" % (
-            self.numPeersToStore - self.numPeersSucceeded, self.numPeersToStore))
-    
-        if self.numPeersSucceeded == 0:
-            self.log(3, "Store timeout - no peers replied, storing locally")
-            self.localNode.storage.putKey(self.keyHashed, self.value, keyIsHashed=True)
-    
-        self.returnValue(True)
-    
-    #@-node:on_tick
-    #@-others
-#@-node:class KRpcStore
-#@-node:STORE
-#@+node:PINGALL
-#@+node:class KRpcPingAll
-class KRpcPingAll(KRpc):
-    """
-    Pings all peers
-    """
-    #@    @+others
-    #@+node:attribs
-    type = 'pingall'
-    #@-node:attribs
-    #@+node:__init__
-    def __init__(self, localNode, client=None, **kw):
-        """
-        Creates and launches a PINGALL rpc
-    
-        Arguments:
-            - localNode - the node performing this RPC
-            - client - see KRpc.__init__
-    
-        Keywords: none
-        """
-        if kw.has_key('cbArgs'):
-            KRpc.__init__(self, localNode, client, cbArgs=kw['cbArgs'])
-        else:
-            KRpc.__init__(self, localNode, client)
-    
-    #@-node:__init__
-    #@+node:start
-    def start(self):
-        """
-        Kicks off this RPC
-        """
-        # launch a findNode rpc against each of our peers
-        peers = self.localNode.peers
-        self.numSent = self.numPending = len(peers)
-        self.numReplied = self.numFailed = 0
-        for peer in peers:
-            KRpcPing(self.localNode, self.on_reply, peer=peer)
-        return
-    
-    #@-node:start
-    #@+node:on_reply
-    def on_reply(self, result):
-        """
-        callback which fires when we get a reply from a STORE we sent to a
-        peer
-        """
-        log(3, "got %s" % repr(result))
-    
-        if result:
-            self.numReplied += 1
-        else:
-            self.numFailed += 1
-        self.numPending -= 1
-    
-        if self.numPending <= 0:
-            res = "pinged:%s replied:%s timeout:%s" % (
-                    self.numSent, self.numReplied, self.numFailed)
-            self.log(3, res)
-            self.returnValue(res)
-    
-    #@-node:on_reply
-    #@+node:on_tick
-    def on_tick(self):
-    
-        self.log(3, "this shouldn't have happened")
-        self.returnValue(False)
-    
-    #@-node:on_tick
-    #@+node:returnValue
-    def returnValue(self, result):
-        """
-        an override with a nicer call sig
-        """
-        # a hack for testing - save this RPC object into the node
-        # so we can introspect it
-        self.localNode.lastrpc = self
-    
-        try:
-            KRpc.returnValue(self, result, status=result)
-        except:
-            traceback.print_exc()
-            self.log(3, "Failed to return %s" % repr(result))
-            KRpc.returnValue(self, 0, status=0)
-    
-    #@-node:returnValue
-    #@-others
-#@-node:class KRpcPingAll
-#@-node:PINGALL
-#@-node:RPC Classes
-#@+node:Node Socket Server
-#@+node:class KNodeServer
-class KNodeServer(KBase, SocketServer.ThreadingMixIn, SocketServer.TCPServer):
-    """
-    Listens for incoming socket connections
-    """
-    #@    @+others
-    #@+node:__init__
-    def __init__(self, node, addr=None):
-    
-        if addr == None:
-            addr = clientAddr
-    
-        self.isRunning = True
-    
-        self.node = node
-    
-        listenHost, listenPort = addr.split(":")
-        listenPort = int(listenPort)
-        self.listenPort = listenPort
-        SocketServer.TCPServer.__init__(self, (listenHost, listenPort), KNodeReqHandler)
-    
-    #@-node:__init__
-    #@+node:serve_forever
-    def serve_forever(self):
-    
-        print "awaiting client connections on port %s" % self.listenPort
-        while self.isRunning:
-            self.handle_request()
-    
-    #@-node:serve_forever
-    #@-others
-#@-node:class KNodeServer
-#@+node:class KNodeReqHandler
-class KNodeReqHandler(KBase, SocketServer.StreamRequestHandler):
-    """
-    Manages a single client connection
-    """
-    #@    @+others
-    #@+node:handle
-    def handle(self):
-        """
-        Conducts all conversation for a single req
-        """
-        req = self.request
-        client = self.client_address
-        server = self.server
-        node = self.server.node
-    
-        read = self.rfile.read
-        readline = self.rfile.readline
-        write = self.wfile.write
-        flush = self.wfile.flush
-        
-        finish = self.finish
-        
-        # start with a greeting
-        write("Stasher version %s ready\n" % version)
-        
-        # get the command
-        line = readline().strip()
-    
-        try:
-            cmd, args = re.split("\\s+", line, 1)
-        except:
-            cmd = line
-            args = ''
-    
-        self.log(3, "cmd=%s args=%s" % (repr(cmd), repr(args)))
-        
-        if cmd in ["get", "getlocal"]:
-            isLocal = cmd == "getlocal"
-            value = node.get(args, local=isLocal)
-            if value == None:
-                write("notfound\n")
-            else:
-                write("ok\n%s\n%s" % (len(value), value))
-            flush()
-            time.sleep(2)
-            finish()
-            return
-        
-        elif cmd in ["put", "putlocal"]:
-            isLocal = cmd == "putlocal"
-            try:
-                size = int(readline())
-                value = read(size)
-                res = node.put(args, value, local=isLocal)
-                if res:
-                    write("ok\n")
-                else:
-                    write("failed\n")
-                flush()
-            except:
-                traceback.print_exc()
-                write("exception\n")
-            finish()
-            return
-    
-        elif cmd == 'addref':
-            try:
-                res = node.addref(args, True)
-                if res:
-                    write("ok\n")
-                else:
-                    write("failed\n")
-                flush()
-            except:
-                traceback.print_exc()
-                write("exception\n")
-            finish()
-            return
-    
-        elif cmd == 'getref':
-            res = node.dest
-            write("ok\n")
-            write("%s\n" % res)
-            flush()
-            time.sleep(1)
-            finish()
-            return
-    
-        elif cmd == 'pingall':
-            res = node._pingall()
-            write(res+"\n")
-            finish()
-            return
-    
-        elif cmd == "die":
-            server.isRunning = False
-            write("server terminated\n")
-            finish()
-    
-        else:
-            write("unrecognisedcommand\n")
-            finish()
-            return
-    
-    #@-node:handle
-    #@+node:finish
-    def finish(self):
-    
-        SocketServer.StreamRequestHandler.finish(self)
-    
-    #@-node:finish
-    #@-others
-#@-node:class KNodeReqHandler
-#@+node:class KNodeClient
-class KNodeClient(KBase):
-    """
-    Talks to a KNodeServer over a socket
-    
-    Subclass this to implement Stasher clients in Python
-    """
-    #@    @+others
-    #@+node:__init__
-    def __init__(self, address=clientAddr):
-    
-        if type(address) in [type(()), type([])]:
-            self.host, self.port = clientAddr
-        else:
-            self.host, self.port = clientAddr.split(":")
-            self.port = int(self.port)
-    
-        self.hello()
-    
-    #@-node:__init__
-    #@+node:hello
-    def hello(self):
-        
-        self.connect()
-        self.close()
-    #@-node:hello
-    #@+node:connect
-    def connect(self):
-    
-        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        self.sock.connect((self.host, self.port))
-    
-        self.rfile = self.sock.makefile("rb")
-        self.read = self.rfile.read
-        self.readline = self.rfile.readline
-        self.wfile = self.sock.makefile("wb")
-        self.write = self.wfile.write
-        self.flush = self.wfile.flush
-    
-        # read greeting
-        greeting = self.readline()
-        parts = re.split("\\s+", greeting)
-        if parts[0] != "Stasher":
-            self.close()
-            raise Exception("Not connected to valid stasher interface")
-    
-    #@-node:connect
-    #@+node:close
-    def close(self):
-        
-        self.rfile.close()
-        #self.wfile.close()
-        self.sock.close()
-    
-    #@-node:close
-    #@+node:get
-    def get(self, key, **kw):
-        """
-        sends a get command to stasher socket, and retrieves
-        and interprets result
-        
-        Arguments:
-            - key - key to retrieve
-        
-        Keywords:
-            - local - default False - if True, only looks in local storage
-        
-        Returns key's value if found, or None if key not found
-        """
-        if kw.get('local', False):
-            cmd = 'getlocal'
-        else:
-            cmd = 'get'
-    
-        self.connect()
-        
-        self.write("%s %s\n" % (cmd, key))
-        self.flush()
-    
-        #print "waiting for resp line"
-        res = self.readline().strip()
-    
-        if res == "ok":
-            size = int(self.readline())
-            val = self.read(size)
-            self.close()
-            return val
-        else:
-            self.close()
-            return None
-    
-    #@-node:get
-    #@+node:put
-    def put(self, key, val, **kw):
-        """
-        Tells remote stasher port to insert a file into the network
-    
-        Arguments:
-            - key - key to insert under
-            - val - value to insert under this key
-        
-        Keywords:
-            - local - default False - if True, only looks in local storage
-        
-        """
-        if kw.get('local', False):
-            cmd = 'putlocal'
-        else:
-            cmd = 'put'
-    
-        self.connect()
-        self.write("%s %s\n" % (cmd, key))
-        self.write("%s\n" % len(val))
-        self.write(val)
-        self.flush()
-        
-        res = self.readline().strip()
-    
-        self.close()
-    
-        if res == "ok":
-            return True
-        else:
-            print repr(res)
-            return False
-    
-    #@-node:put
-    #@+node:addref
-    def addref(self, ref):
-        """
-        Passes a new noderef to node
-        """
-        self.connect()
-        self.write("addref %s\n" % ref)
-        self.flush()
-        
-        res = self.readline().strip()
-    
-        self.close()
-    
-        if res == "ok":
-            return True
-        else:
-            print repr(res)
-            return False
-    
-    #@-node:addref
-    #@+node:getref
-    def getref(self):
-        """
-        Uplifts node's own ref
-        """
-        self.connect()
-        self.write("getref\n")
-        self.flush()
-        
-        res = self.readline().strip()
-    
-        if res == "ok":
-            ref = self.readline().strip()
-            self.close()
-            return ref
-        else:
-            self.close()
-            return "failed"
-    
-    #@-node:getref
-    #@+node:pingall
-    def pingall(self):
-        """
-        Uplifts node's own ref
-        """
-        self.connect()
-        self.write("pingall\n")
-        self.flush()
-        
-        res = self.readline().strip()
-    
-        self.close()
-    
-        return res
-    
-    
-    #@-node:pingall
-    #@+node:kill
-    def kill(self):
-        """
-        Tells remote server to fall on its sword
-        """
-        try:
-            while 1:
-                self.connect()
-                self.write("die\n")
-                self.flush()
-                self.close()
-        except:
-            pass
-    
-    
-    #@-node:kill
-    #@+node:__getitem__
-    def __getitem__(self, item):
-        
-        return self.get(item)
-    
-    #@-node:__getitem__
-    #@+node:__setitem__
-    def __setitem__(self, item, val):
-        
-        if not self.put(item, val):
-            raise Exception("Failed to insert")
-    
-    #@-node:__setitem__
-    #@-others
-
-#@-node:class KNodeClient
-#@-node:Node Socket Server
-#@+node:NODE
-#@+node:class KNode
-class KNode(KBase):
-    """
-    B{Public API to this Kademlia implementation}
-    
-    You should not normally need to use, or even be aware of, 
-    any of the other classes
-
-    And in this class, the only methods you need to worry about are:
-        - L{start} - starts the node running
-        - L{stop} - stops the node
-        - L{get} - retrieve a key value
-        - L{put} - stores a key value
-        - L{addref} - imports a noderef
-
-    This class implements a single kademlia node.
-    Within a single process, you can create as many nodes as you like.
-    """
-    #@    @+others
-    #@+node:attributes
-    SocketFactory = None # defaults to I2P socket
-    
-    #@-node:attributes
-    #@+node:__init__
-    def __init__(self, name, **kw):
-        """
-        Creates a kademlia node of name 'name'.
-    
-        Name is mandatory, because each name is permanently written
-        to the SAM bridge's store
-        
-        I thought of supporting random name generation, but went off this
-        idea because names get permanently stored to SAM bridge's file
-    
-        Arguments:
-            - name - mandatory - a short text name for the node, should
-              be alphanumerics, '-', '.', '_'
-              This name is used for the SAM socket session.
-        
-        Keywords:
-            - storage - optional - an instance of L{KStorageBase} or one of
-              its subclasses. If not given, default action is to instantiate
-              a L{KStorageFile} object against the given node name
-        """
-        # remember who we are
-        self.name = name
-    
-        # not running yet, will launch when explicitly started, or implicitly
-        # when the first operation gets done
-        self.isRunning = False
-    
-        # create socket and get its dest, and determine our node id
-        self.id = KHash("<NONE>")
-        self.log(5, "creating socket for node %s" % name)
-        self.log(5, "socket for node %s created" % name)
-        if self.SocketFactory == None:
-            self.SocketFactory = i2p.socket.socket
-        self.sock = self.SocketFactory(
-            "stashernode-"+name,
-            i2p.socket.SOCK_DGRAM,
-            samaddr=samAddr,
-            **kw)
-        #self.sockLock = threading.Lock() # prevents socket API reentrance
-        self.sock.setblocking(0)
-        self.dest = self.sock.dest
-        self.id = KHash(self.dest)
-    
-        # create our buckets
-        self.buckets = []
-        for i in range(160):
-            self.buckets.append(KBucket())
-        
-        # create our storage object, default to new instance of KStorageFile
-        self.storage = kw.get('storage', KStorageFile(self))
-    
-        # dig out all previously known nodes
-        self.peers = self.storage.getRefs()
-    
-        # set up dict of callers awaiting replies
-        # keys are (peerobj, msgId) tuples, values are Queue.Queue objects
-        self.pendingPings = {}
-    
-        # mapping of (peer, msgId) to RPC object, so when RPC replies come in,
-        # they can be passed directly to the RPC object concerned
-        self.rpcBindings = {}
-    
-        # KRpc objects waiting for peer replies - used for checking for timeouts
-        self.rpcPending = []
-        
-        # miscellaneous shit
-        self._msgIdNext = 0
-        #self._msgIdLock = threading.Lock()
-    
-        # register in global map
-        _nodes[name] = self
-    
-    
-    #@-node:__init__
-    #@+node:__del__
-    def __del__(self):
-        """
-        Cleanup
-        """
-    
-    #@-node:__del__
-    #@+node:application-level
-    #@+node:start
-    def start(self, doPings=True):
-        """
-        Starts the node running
-        """
-        # barf if already running
-        if self.isRunning:
-            self.log(3, "node %s is already running!" % self.name)
-            return
-    
-        self.log(3, "starting node %s" % self.name)
-    
-        # first step - ping all our peers
-        if doPings:
-            for peer in self.peers:
-                self.log(3, "doing initial ping\n%s\n%s" % (self, peer))
-                KRpcPing(self, peer=peer)
-    
-        # first step - do a findNode against our own node id, and ping our
-        # neighbours
-        if greetPeersOnStartup:
-            neighbours = KRpcFindNode(self, hash=self.id).execute()
-            self.log(3, "neighbours=%s" % repr([n[:10] for n in neighbours]))
-            for n in neighbours:
-                n = self._normalisePeer(n)
-                KRpcPing(self, peer=n)
-    
-        # note now that we're running
-        self.isRunning = True
-    
-        # and enlist with the core
-        if runCore:
-            core.subscribe(self)
-        else:
-            # central core disabled, run our own receiver thread instead
-            thread.start_new_thread(self._threadRx, ())
-    #@-node:start
-    #@+node:stop
-    def stop(self):
-        """
-        Shuts down the node
-        """
-        self.isRunning = 0
-        if runCore:
-            try:
-                core.unsubscribe(self)
-            except:
-                pass
-    #@-node:stop
-    #@+node:get
-    def get(self, item, callback=None, **kw):
-        """
-        Attempts to retrieve data from the network
-        
-        Arguments:
-            - item - the key we desire
-            - callback - optional - if given, the get will be performed
-              asynchronously, and callback will be invoked upon completion, with
-              the result as first argument
-        Keywords:
-            - local - optional - if True, limits this search to this local node
-              default is False
-        
-        Returns:
-            - if no callback - the item value if the item was found, or None if not
-            - if callback, None is returned
-        """
-        def processResult(r):
-            if isinstance(r, str):
-                return r
-            return None
-    
-        if callback:
-            # create a func to process callback result
-            def onCallback(res):
-                callback(processResult(res))
-        
-            self._finddata(item, onCallback, **kw)
-        else:
-            return processResult(self._finddata(item, **kw))
-    
-    #@-node:get
-    #@+node:put
-    def put(self, key, value, callback=None, **kw):
-        """
-        Inserts a named key into the network
-    
-        Arguments:
-            - key - one of:
-                - None - a secure key will be generated and used
-                - a KHash object
-                - a raw string which will be hashed into a KHash object
-            - val - a string, the value associated with the key
-    
-        Keywords:
-            - local - default False - if True, limits the insert to the
-              local node
-    
-        If the value is larger than L{maxValueSize}, a L{KValueTooLarge}
-        exception will occur.
-        """
-        return self._store(key, value, callback, **kw)
-    
-    #@-node:put
-    #@+node:addref
-    def addref(self, peer, doPing=False):
-        """
-        Given a peer node's destination, add it to our
-        buckets and internal data store
-        
-        Arguments:
-            - peer - one of:
-                - the I2P destination of the peer node, as
-                  a base64 string
-                - a KNode object
-                - a KPeer object
-            - doPing - ping this node automatically (default False)
-        """
-        peer = self._normalisePeer(peer)
-    
-        # remember peer if not already known
-        if peer.dest == self.dest:
-            self.log(3, "node %s, trying to add ref to ourself???" % self.name)
-            return peer
-        elif not self._findPeer(peer.dest):
-            self.peers.append(peer)
-            self.storage.putRefs(peer)
-        else:
-            self.log(4, "node %s, trying to add duplicate noderef %s" % (
-                self.name, peer))
-            return peer
-    
-        # update our KBucket
-        dist = self.id.distance(peer.id)
-        self.buckets[dist].justSeenPeer(peer)
-    
-        if doPing:
-            self.log(4, "doing initial ping\n%s\n%s" % (self, peer))
-            KRpcPing(self, peer=peer)
-    
-        return peer
-    
-    #@-node:addref
-    #@+node:__getitem__
-    def __getitem__(self, item):
-        """
-        Allows dict-like accesses on the node object
-        """
-        return self.get(item)
-    #@-node:__getitem__
-    #@+node:__setitem__
-    def __setitem__(self, item, val):
-        """
-        Allows dict-like key setting on the node object
-        """
-        self.put(item, val)
-    
-    #@-node:__setitem__
-    #@-node:application-level
-    #@+node:peer/rpc methods
-    #@+node:_ping
-    def _ping(self, peer=None, callback=None, **kw):
-        """
-        Sends a ping to remote peer, and awaits response
-        
-        Not of much real use to application level, except
-        perhaps for testing
-    
-        If the argument 'peer' is not given, the effect is to 'ping the
-        local node', which I guess might be a bit silly
-    
-        The second argument 'callback' is a callable, which if given, makes this
-        an asynchronous (non-blocking) call, in which case the callback will be
-        invoked upon completion (or timeout).
-        
-        If the keyword 'cbArgs' is given in addition to the callback, the callback
-        will fire with the results as first argument and this value as second arg
-        """
-        if callback:
-            KRpcPing(self, callback, peer=peer, **kw)
-        else:
-            return KRpcPing(self, peer=peer).execute()
-    
-    #@-node:_ping
-    #@+node:_pingall
-    def _pingall(self, callback=None):
-        """
-        Sends a ping to all peers, returns text string on replies/failures
-        """
-        if callback:
-            KRpcPingAll(self, callback, **kw)
-        else:
-            return KRpcPingAll(self).execute()
-    
-       
-    #@-node:_pingall
-    #@+node:_findnode
-    def _findnode(self, something=None, callback=None, **kw):
-        """
-        Mainly for testing - does a findNode query on the network
-        
-        Arguments:
-            - something - one of:
-                - plain string - the string gets hashed and used for the search
-                - int or long int - this gets used as the raw hash
-                - a KHash object - that's what gets used
-                - None - the value of the 'raw' keyword will be used instead
-            - callback - optional - if given, a callable object which will be
-              called upon completion, with the result as argument
-    
-        Keywords:
-            - local - optional - if True, only returns the closest peers known to
-              node. if False, causes node to query other nodes.
-              default is False
-            - raw - one of:
-                - 20-byte string - this gets used as a binary hash
-                - 40-byte string - this gets used as a hex hash
-        """
-        if not kw.has_key('local'):
-            kw = dict(kw)
-            kw['local'] = False
-    
-        self.log(3, "about to instantiate findnode rpc")
-        if callback:
-            KRpcFindNode(self, callback, hash=something, **kw)
-            self.log(3, "asynchronously invoked findnode, expecting callback")
-        else:
-            lst = KRpcFindNode(self, hash=something, **kw).execute()
-            self.log(3, "back from findnode rpc")
-            res = [self._normalisePeer(p) for p in lst] # wrap in KPeer objects
-            return res
-    
-    #@-node:_findnode
-    #@+node:_finddata
-    def _finddata(self, something=None, callback=None, **kw):
-        """
-        As for findnode, but if data is found, return the data instead
-        """
-        if not kw.has_key('local'):
-            kw = dict(kw)
-            kw['local'] = False
-    
-        self.log(3, "about to instantiate finddata rpc")
-        if callback:
-            KRpcFindData(self, callback, hash=something, **kw)
-            self.log(3, "asynchronously invoked finddata, expecting callback")
-        else:
-            res = KRpcFindData(self, hash=something, **kw).execute()
-            self.log(3, "back from finddata rpc")
-            if not isinstance(res, str):
-                self.log(4, "findData RPC returned %s" % repr(res))
-                res = [self._normalisePeer(p) for p in res] # wrap in KPeer objects
-            return res
-    
-    #@-node:_finddata
-    #@+node:_store
-    def _store(self, key, value, callback=None, **kw):
-        """
-        Performs a STORE rpc
-        
-        Arguments:
-            - key - string - text name of key
-            - value - string - value to store
-    
-        Keywords:
-            - local - if given and true, only store value onto local store
-        """
-        if not kw.has_key('local'):
-            kw = dict(kw)
-            kw['local'] = False
-    
-        key = shahash(key)
-        if callback:
-            KRpcStore(self, callback, key=key, value=value, **kw)
-            self.log(3, "asynchronously invoked findnode, expecting callback")
-        else:
-            res = KRpcStore(self, key=key, value=value, **kw).execute()
-            return res
-    
-    #@-node:_store
-    #@+node:_findPeer
-    def _findPeer(self, dest):
-        """
-        Look up our table of current peers for a given dest.
-        
-        If dest is found, return its object, otherwise return None
-        """
-        for peerObj in self.peers:
-            if peerObj.dest == dest:
-                return peerObj
-        return None
-    
-    #@-node:_findPeer
-    #@-node:peer/rpc methods
-    #@+node:comms methods
-    #@+node:_sendRaw
-    def _sendRaw(self, peer, **kw):
-        """
-        Serialises keywords passed, and sends this as a datagram
-        to node 'peer'
-        """
-        # update our KBucket
-        dist = self.id.distance(peer.id)
-        self.buckets[dist].justSeenPeer(peer)
-    
-        # save ref to this peer
-        self.addref(peer)
-    
-        params = dict(kw)
-        msgId = params.get('msgId', None)
-        if msgId == None:
-            msgId = params['msgId'] = self._msgIdAlloc()
-    
-        objenc = messageEncode(params)
-        self.log(5, "node %s waiting for send lock" % self.name)
-        #self.sockLock.acquire()
-        self.log(5, "node %s got send lock" % self.name)
-        try:
-            self.sock.sendto(objenc, 0, peer.dest)
-        except:
-            traceback.print_exc()
-        #self.sockLock.release()
-        self.log(5, "node %s released send lock" % self.name)
-    
-        self.log(4, "node %s sent %s to peer %s" % (self.name, params, peer.dest))
-        return msgId
-    
-    #@-node:_sendRaw
-    #@-node:comms methods
-    #@+node:engine
-    #@+node:_threadRx
-    def _threadRx(self):
-        """
-        Thread which listens for incoming datagrams and actions
-        accordingly
-        """
-        self.log(3, "starting receiver thread for node %s" % self.name)
-    
-        try:
-            # loop to drive the node
-            while self.isRunning:
-                self._doChug()
-        except:
-            traceback.print_exc()
-            self.log(3, "node %s - THREAD CRASH!" % self.name)
-    
-        self.log(3, "receiver thread for node %s terminated" % self.name)
-    
-    #@-node:_threadRx
-    #@+node:_doChug
-    def _doChug(self):
-        """
-        Do what's needed to drive the node.
-        Handle incoming packets
-        Check on and action timeouts
-        """
-        # handle all available packets
-        while self._doRx():
-            pass
-    
-        # do maintenance - eg processing timeouts
-        self._doHousekeeping()
-    
-    #@-node:_doChug
-    #@+node:_doRx
-    def _doRx(self):
-        """
-        Receives and handles one incoming packet
-        
-        Returns True if a packet got handled, or False if timeout
-        """
-        # get next packet
-        self.log(5, "%s seeking socket lock" % self.name)
-        #self.sockLock.acquire()
-        self.log(5, "%s got socket lock" % self.name)
-        try:
-            item = self.sock.recvfrom(-1)
-        except i2p.socket.BlockError:
-            #self.sockLock.release()
-            self.log(5, "%s released socket lock after timeout" % self.name)
-            if not runCore:
-                time.sleep(0.1)
-            return False
-        except:
-            traceback.print_exc()
-            self.log(5, "%s released socket lock after exception" % self.name)
-            #self.sockLock.release()
-            return True
-        #self.sockLock.release()
-        self.log(5, "%s released socket lock normally" % self.name)
-    
-        try:
-            (data, dest) = item
-        except ValueError:
-            self.log(3, "node %s: recvfrom returned no dest, possible spoof" \
-                % self.name)
-            data = item[0]
-            dest = None
-    
-        # try to decode
-        try:
-            d = messageDecode(data)
-        except:
-            traceback.print_exc()
-            self.log(3, "failed to unpickle incoming data for node %s" % \
-                self.name)
-            return True
-    
-        # ditch if not a dict
-        if type(d) != type({}):
-            self.log(3, "node %s: decoded packet is not a dict" % self.name)
-            return True
-    
-        # temporary workaround for sam socket bug    
-        if dest == None:
-            if hasattr(d, 'has_key') and d.has_key('dest'):
-                dest = d['dest']
-    
-        # try to find it in our store
-        peerObj = self._findPeer(dest)
-        if peerObj == None:
-            # previously unknown peer - add it to our store
-            peerObj = self.addref(dest)
-        else:
-            peerObj.justSeen() # already exists - refresh its timestamp
-            self.addref(peerObj.dest)
-    
-        # drop packet if no msgId
-        msgId = d.get('msgId', None)
-        if msgId == None:
-            self.log(3, "no msgId, dropping")
-            return True
-        del d['msgId']
-    
-        msgType = d.get('type', 'unknown')
-    
-        if desperatelyDebugging:
-            pass
-            #set_trace()
-    
-        # if a local RPC is awaiting this message, fire its callback
-        item = self.rpcBindings.get((peerObj.dest, msgId), None)
-        if item:
-            rpc, peer = item
-            try:
-                rpc.unbindPeerReply(peerObj, msgId)
-                if desperatelyDebugging:
-                    set_trace()
-                rpc.on_reply(peerObj, msgId, **d)
-    
-            except:
-                traceback.print_exc()
-                self.log(2, "unhandled exception in RPC on_reply")
-        else:
-            # find a handler, fallback on 'unknown'
-            self.log(5, "\nnode %s\ngot msg id %s type %s:\n%s" % (
-                self.name, msgId, msgType, d))
-            hdlrName = d.get('type', 'unknown')
-            hdlr = getattr(self, "_on_"+hdlrName)
-            try:
-                if desperatelyDebugging:
-                    set_trace()
-                hdlr(peerObj, msgId, **d)
-            except:
-                traceback.print_exc()
-                self.log(2, "unhandled exception in unbound packet handler %s" % hdlrName)
-    
-        return True
-    
-    #@-node:_doRx
-    #@+node:_doHousekeeping
-    def _doHousekeeping(self):
-        """
-        Performs periodical housekeeping on this node.
-        
-        Activities include:
-            - checking pending records for timeouts
-        """
-        now = time.time()
-    
-        # DEPRECATED - SWITCH TO RPC-based
-        # check for expired pings
-        for msgId, (dest, q, pingDeadline) in self.pendingPings.items():
-    
-            if pingDeadline > now:
-                # not timed out, leave in pending
-                continue
-    
-            # ping has timed out
-            del self.pendingPings[msgId]
-            q.put(False)
-    
-        # check for timed-out RPCs
-        for rpc in self.rpcPending[:]:
-            if rpc.nextTickTime != None and now >= rpc.nextTickTime:
-                try:
-                    rpc.on_tick()
-                except:
-                    traceback.print_exc()
-                    self.log(2, "unhandled exception in RPC on_tick")
-    
-    #@-node:_doHousekeeping
-    #@-node:engine
-    #@+node:event handling
-    #@+others
-    #@+node:_on_ping
-    def _on_ping(self, peer, msgId, **kw):
-        """
-        Handler for ping received events
-        """
-        KRpcPing(self, (peer, msgId), local=True, **kw)
-        return
-        
-        # old stuff
-    
-        self.log(3, "\nnode %s\nfrom %s\nreceived:\n%s" % (self.name, peer, kw))
-    
-        # randomly cause ping timeouts if testing
-        if testing:
-            howlong = random.randint(0, 5)
-            self.log(3, "deliberately pausing for %s seconds" % howlong)
-            time.sleep(howlong)
-    
-        # pong back to node
-        peer.send_reply(msgId=msgId)
-    
-        
-    #@nonl
-    #@-node:_on_ping
-    #@+node:_on_findNode
-    def _on_findNode(self, peer, msgId, **kw):
-        """
-        Handles incoming findNode command
-        """
-        KRpcFindNode(self, (peer, msgId), local=True, **kw)
-    
-    #@-node:_on_findNode
-    #@+node:_on_findData
-    def _on_findData(self, peer, msgId, **kw):
-        """
-        Handles incoming findData command
-        """
-        KRpcFindData(self, (peer, msgId), local=True, **kw)
-    
-    #@-node:_on_findData
-    #@+node:_on_store
-    def _on_store(self, peer, msgId, **kw):
-        """
-        Handles incoming STORE command
-        """
-        self.log(4, "got STORE rpc from upstream:\npeer=%s\nmsgId=%s\nkw=%s" % (peer, msgId, kw))
-    
-        KRpcStore(self, (peer, msgId), local=True, **kw)
-    
-    #@-node:_on_store
-    #@+node:_on_reply
-    def _on_reply(self, peer, msgId, **kw):
-        """
-        This should never happen
-        """
-        self.log(4, "got unhandled reply:\npeer=%s\nmsgId=%s\nkw=%s" % (
-            peer, msgId, kw))
-    
-    #@-node:_on_reply
-    #@+node:_on_unknown
-    def _on_unknown(self, peer, msgId, **kw):
-        """
-        Handler for unknown events
-        """
-        self.log(3, "node %s from %s received msgId=%s:\n%s" % (
-            self.name, peer, msgId, kw))
-    
-    #@-node:_on_unknown
-    #@-others
-    #@-node:event handling
-    #@+node:Socket Client Server
-    #@+node:serve
-    def serve(self):
-        """
-        makes this node listen on socket for incoming client
-        connections, and services these connections
-        """
-        server = KNodeServer(self)
-        server.serve_forever()
-    
-    #@-node:serve
-    #@-node:Socket Client Server
-    #@+node:lowlevel stuff
-    #@+others
-    #@+node:__str__
-    def __str__(self):
-        return "<KNode:%s=0x%s...>" % (
-            self.name,
-            ("%x" % self.id.value)[:8],
-            )
-    #@-node:__str__
-    #@+node:__repr__
-    def __repr__(self):
-        return str(self)
-    
-    #@-node:__repr__
-    #@+node:_msgIdAlloc
-    def _msgIdAlloc(self):
-        """
-        issue a new and unique message id
-        """
-        #self._msgIdLock.acquire()
-        msgId = self._msgIdNext
-        self._msgIdNext += 1
-        #self._msgIdLock.release()
-        return msgId
-    #@-node:_msgIdAlloc
-    #@+node:_normalisePeer
-    def _normalisePeer(self, peer):
-        """
-        Takes either a b64 dest string, a KPeer object or a KNode object,
-        and returns a KPeer object
-        """
-        # act according to whatever type we're given
-        if isinstance(peer, KPeer):
-            return peer # already desired format
-        elif isinstance(peer, KNode):
-            return KPeer(self, peer.dest)
-        elif isinstance(peer, str) and len(peer) > 256:
-            return KPeer(self, peer)
-        else:
-            self.log(3, "node %s, trying to add invalid noderef %s" % (
-                self.name, peer))
-            raise KBadNode(peer)
-    
-    #@-node:_normalisePeer
-    #@+node:__del__
-    def __del__(self):
-        """
-        Clean up on delete
-        """
-        self.log(3, "node dying: %s" % self.name)
-    
-        try:
-            del _nodes[self.name]
-        except:
-            pass
-    
-        self.stop()
-    
-    #@-node:__del__
-    #@-others
-    #@-node:lowlevel stuff
-    #@-others
-#@-node:class KNode
-#@-node:NODE
-#@+node:funcs
-#@+others
-#@+node:userI2PDir
-def userI2PDir(nodeName=None):
-    """
-    Returns a directory under user's home dir into which
-    stasher files can be written
-
-    If nodename is given, a subdirectory will be found/created
-    
-    Return value is toplevel storage dir if nodename not given, 
-    otherwise absolute path including node dir
-    """
-    if dataDir != None:
-        if not os.path.isdir(dataDir):
-            os.makedirs(dataDir)
-        return dataDir
-
-    if sys.platform == 'win32':
-        home = os.getenv("APPDATA")
-        if home:
-            topDir = os.path.join(home, "stasher")
-        else:
-            topDir = os.path.join(os.getcwd(), "stasher")
-    else:
-        #return os.path.dirname(__file__)
-        topDir = os.path.join(os.path.expanduser('~'), ".stasher")
-
-    if not os.path.isdir(topDir):
-        os.makedirs(topDir)
-    if nodeName == None:
-        return topDir
-    else:
-        nodeDir = os.path.join(topDir, nodeName)
-        if not os.path.isdir(nodeDir):
-            os.makedirs(nodeDir)
-        return nodeDir
-
-#@-node:userI2PDir
-#@+node:nodePidfile
-def nodePidfile(nodename):
-    return os.path.join(userI2PDir(nodename), "node.pid")
-
-#@-node:nodePidfile
-#@+node:messageEncode
-def messageEncode(params):
-    """
-    Serialise the dict 'params' for sending
-    
-    Temporarily using bencode - replace later with a more
-    efficient struct-based impl.
-    """
-    try:
-        return bencode.bencode(params)
-    except:
-        log(1, "encoder failed to encode: %s" % repr(params))
-        raise
-
-#@-node:messageEncode
-#@+node:messageDecode
-def messageDecode(raw):
-    return bencode.bdecode(raw)
-#@-node:messageDecode
-#@+node:shahash
-def shahash(somestr, bin=False):
-    shaobj = sha.new(somestr)
-    if bin:
-        return shaobj.digest()
-    else:
-        return shaobj.hexdigest()
-
-#@-node:shahash
-#@+node:log
-logLock = threading.Lock()
-
-def log(verbosity, msg, nPrev=0, clsname=None):
-
-    global logToSocket, logFile
-
-    # create logfile if not exists
-    if logFile == None:
-        logFile = os.path.join(userI2PDir(), "stasher.log")
-
-    # rip the stack
-    caller = traceback.extract_stack()[-(2+nPrev)]
-    path, line, func = caller[:3]
-    path = os.path.split(path)[1]
-
-    #print "func is type %s, val %s" % (type(func), repr(func))
-
-    #if hasattr(func, "im_class"):
-    #    func = 
-
-    if clsname:
-        func = clsname + "." + func
-
-    #msg = "%s:%s:%s():  %s" % (
-    #    path,
-    #    line,
-    #    func,
-    #    msg.replace("\n", "\n   + "))
-
-    msg = "%s():%s:  %s" % (
-        func,
-        line,
-        msg.replace("\n", "\n   + "))
-
-    # do better logging later
-    if verbosity > logVerbosity:
-        return
-
-    if logToSocket:
-        try:
-            if isinstance(logToSocket, int):
-                portnum = logToSocket
-                logToSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-                connected = 0
-                while 1:
-                    try:
-                        logToSocket.connect(("localhost", portnum))
-                        break
-                    except socket.error:
-                        print "Please open an xterm/nc listening on %s" % logToSocket
-                        time.sleep(1)
-
-            logToSocket.send(msg+"\n")
-        except:
-            traceback.print_exc()
-    else:
-        print msg
-
-    logLock.acquire()
-    file(logFile, "a+").write(msg + "\n")
-    logLock.release()
-#@-node:log
-#@+node:logexc
-def logexc(verbosity, msg, nPrev=0, clsname=None):
-
-    fd = StringIO("%s\n" % msg)
-    traceback.print_exc(file=fd)
-    log(verbosity, fd.getvalue(), nPrev, clsname)
-
-#@-node:logexc
-#@+node:spawnproc
-def spawnproc(*args, **kw):
-    """
-    Spawns a process and returns its PID
-    
-    VOMIT!
-    
-    I have to do a pile of odious for the win32 side
-    
-    Returns a usable PID
-    
-    Keywords:
-        - priority - priority at which to spawn - default 20 (highest)
-    """
-    # get priority, convert to a unix 'nice' value
-    priority = 20 - kw.get('priority', 20)
-    
-    if sys.platform != 'win32':
-        # *nix - easy
-        #print "spawnproc: launching %s" % repr(args)
-        
-        # insert nice invocation
-        args = ['/usr/bin/nice', '-n', str(priority)] + list(args)
-        return os.spawnv(os.P_NOWAIT, args[0], args)
-
-    else:
-        # just close your eyes here and pretend this abomination isn't happening! :((
-        args = list(args)
-        args.insert(0, sys.executable)
-        cmd = " ".join(args)
-        #print "spawnproc: launching %s" % repr(cmd)
-
-        if 0:
-            try:
-                c =  _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE)
-                c1 = _winreg.OpenKey(c, "SOFTWARE")
-                c2 = _winreg.OpenKey(c1, "Microsoft")
-                c3 = _winreg.OpenKey(c2, "Windows NT")
-                c4 = _winreg.OpenKey(c3, "CurrentVersion")
-                supportsBelowNormalPriority = 1
-            except:
-                supportsBelowNormalPriority = 0
-        else:
-            if sys.getwindowsversion()[3] != 2:
-                supportsBelowNormalPriority = 0
-            else:
-                supportsBelowNormalPriority = 1
-    
-        # frig the priority into a windows value
-        if supportsBelowNormalPriority:
-            if priority < 7:
-                pri = win32process.IDLE_PRIORITY_CLASS
-            elif priority < 14:
-                pri = 0x4000
-            else:
-                pri = win32process.NORMAL_PRIORITY_CLASS
-        else:
-            if priority < 11:
-                pri = win32process.IDLE_PRIORITY_CLASS
-            else:
-                pri = win32process.NORMAL_PRIORITY_CLASS
-    
-        print "spawnproc: launching %s" % repr(args)
-        si = win32process.STARTUPINFO()
-        hdl = win32process.CreateProcess(
-            sys.executable, # lpApplicationName
-            cmd,  # lpCommandLine
-            None,  # lpProcessAttributes
-            None, # lpThreadAttributes
-            0,    # bInheritHandles
-            0,    # dwCreationFlags
-            None, # lpEnvironment
-            None, # lpCurrentDirectory
-            si,   # lpStartupInfo
-            )
-        pid = hdl[2]
-        #print "spawnproc: pid=%s" % pid
-        return pid
-#@-node:spawnproc
-#@+node:killproc
-def killproc(pid):
-    if sys.platform == 'win32':
-        print repr(pid)
-        handle = win32api.OpenProcess(1, 0, pid)
-        print "pid %s -> %s" % (pid, repr(handle))
-        #return (0 != win32api.TerminateProcess(handle, 0))
-        win32process.TerminateProcess(handle, 0)
-    else:
-        return os.kill(pid, signal.SIGKILL)
-#@-node:killproc
-#@+node:i2psocket
-def i2psocket(self, *args, **kw):
-    return i2p.socket.socket(*args, **kw)
-
-#@-node:i2psocket
-#@+node:usage
-def usage(detailed=False, ret=0):
-    
-    print "Usage: %s <options> [<command> [<ars>...]]" % sys.argv[0]
-    if not detailed:
-        print "Type %s -h for help" % sys.argv[0]
-        sys.exit(ret)
-
-    print "This is stasher, distributed file storage network that runs"
-    print "atop the anonymising I2P network (http://www.i2p.net)"
-    print "Written by aum - August 2004"
-    print
-    print "Options:"
-    print "  -h, --help              - display this help"
-    print "  -v, --version           - print program version"
-    print "  -V, --verbosity=n       - verbosity, default 1, 1=quiet ... 4=noisy"
-    print "  -S, --samaddr=host:port - host:port of I2P SAM port, "
-    print "                            default %s" % i2p.socket.samaddr
-    print "  -C, --clientaddr=host:port - host:port for socket interface to listen on"
-    print "                            for clients, default %s" % clientAddr
-    print "  -d, --datadir=dir       - directory in which stasher files get written"
-    print "                            default is ~/.stasher"
-    print "  -f, --foreground        - only valid for 'start' cmd - runs the node"
-    print "                            in foreground without spawning - for debugging"
-    print "  -l, --localonly         - only valid for get/put - restricts the get/put"
-    print "                            operation to the local node only"
-    print
-    print "Commands:"
-    print "  start [<nodename>]"
-    print "    - launches a single node, which forks off and runs in background"
-    print "      nodename is a short unique nodename, default is '%s'" % defaultNodename
-    print "  stop [<nodename>]"
-    print "    - terminates running node <nodename>"
-    print "  get <keyname> [<file>]"
-    print "    - attempts to retrieve key <keyname> from the network, saving"
-    print "      to file <file> if given, or to stdout if not"
-    print "  put <keyname> [<file>]"
-    print "    - inserts key <keyname> into the network, taking its content"
-    print "      from file <file> if given, otherwise reads content from stdin"
-    print "  addref <file>"
-    print "    - adds a new noderef to the node, taking the base64 noderef"
-    print "      from file <file> if given, or from stdin"
-    print "      (if you don't have any refs, visit http://stasher.i2p, or use"
-    print "      the dest in the file aum.stasher in cvs)"
-    print "  getref <file>"
-    print "    - uplifts the running node's dest as base64, writing it to file"
-    print "      <file> if given, or to stdout"
-    print "  hello"
-    print "    - checks that local node is running"
-    print "  pingall"
-    print "    - diagnostic tool - pings all peers, waits for replies or timeouts,"
-    print "      reports results"
-    print "  help"
-    print "    - display this help"
-    print
-
-    sys.exit(0)
-
-#@-node:usage
-#@+node:err
-def err(msg):
-    sys.stderr.write(msg+"\n")
-#@-node:err
-#@+node:main
-def main():
-    """
-    Command line interface
-    """
-    global samAddr, clientAddr, logVerbosity, dataDir
-
-    argv = sys.argv
-    argc = len(argv)
-
-    try:
-        opts, args = getopt.getopt(sys.argv[1:],
-                                   "h?vV:S:C:sd:fl",
-                                   ['help', 'version', 'samaddr=', 'clientaddr=',
-                                    'verbosity=', 'status', 'datadir=', 'foreground',
-                                    'shortversion', 'localonly',
-                                    ])
-    except:
-        traceback.print_exc(file=sys.stdout)
-        usage("You entered an invalid option")
-
-    daemonise = True
-    verbosity = 2
-    debug = False
-    foreground = False
-    localOnly = False
-
-    for opt, val in opts:
-        
-        if opt in ['-h', '-?', '--help']:
-            usage(True)
-
-        elif opt in ['-v', '--version']:
-            print "Stasher version %s" % version
-            sys.exit(0)
-
-        elif opt in ['-V', '--verbosity']:
-            logVerbosity = int(val)
-
-        elif opt in ['-f', '--foreground']:
-            foreground = True
-        
-        elif opt in ['-S', '--samaddr']:
-            samAddr = val
-
-        elif opt in ['-C', '--clientaddr']:
-            clientAddr = val
-        
-        elif opt in ['-s', '--status']:
-            dumpStatus()
-
-        elif opt in ['-d', '--datadir']:
-            dataDir = val
-
-        elif opt == '--shortversion':
-            sys.stdout.write("%s" % version)
-            sys.stdout.flush()
-            sys.exit(0)
-
-        elif opt in ['-l', '--localonly']:
-            localOnly = True
-
-    #print "Debug - bailing"
-    #print repr(opts)
-    #print repr(args)
-    #sys.exit(0)
-
-    # Barf if no command given
-    if len(args) == 0:
-        err("No command given")
-        usage(0, 1)
-
-    cmd = args.pop(0)
-    argc = len(args)
-
-    #print "cmd=%s, args=%s" % (repr(cmd), repr(args))
-    
-    if cmd not in ['help', '_start', 'start', 'stop',
-                   'hello', 'get', 'put', 'addref', 'getref',
-                   'pingall']:
-        err("Illegal command '%s'" % cmd)
-        usage(0, 1)
-
-    if cmd == 'help':
-        usage()
-
-    # dirty hack
-    if foreground and cmd == 'start':
-        cmd = '_start'
-
-    # magic undocumented command name - starts node, launches its client server,
-    # this should only happen if we're spawned from a 'start' command
-    if cmd == '_start':
-        if argc not in [0, 1]:
-            err("start: bad argument count")
-            usage()
-        if argc == 0:
-            nodeName = defaultNodename
-        else:
-            nodeName = args[0]
-        
-        # create and serve a node
-        #set_trace()
-        node = KNode(nodeName)
-        node.start()
-        log(3, "Node %s launched, dest = %s" % (node.name, node.dest))
-        node.serve()
-        sys.exit(0)
-    
-    if cmd == 'start':
-        if argc not in [0, 1]:
-            err("start: bad argument count")
-            usage()
-        if argc == 0:
-            nodeName = defaultNodename
-        else:
-            nodeName = args[0]
-        pidFile = nodePidfile(nodeName)
-
-        if os.path.exists(pidFile):
-            err(("Stasher node '%s' seems to be already running. If you are\n" % nodeName)
-                +"absolutely sure it's not running, please remove its pidfile:\n"
-                +pidFile+"\n")
-            sys.exit(1)
-
-        # spawn off a node
-        import stasher
-        pid = spawnproc(sys.argv[0], "-S", samAddr, "-C", clientAddr, "_start", nodeName)
-        file(pidFile, "wb").write("%s" % pid)
-        print "Launched stasher node as pid %s" % pid
-        print "Pidfile is %s" % pidFile
-        sys.exit(0)
-
-    if cmd == 'stop':
-        if argc not in [0, 1]:
-            err("stop: bad argument count")
-            usage()
-        if argc == 0:
-            nodeName = defaultNodename
-        else:
-            nodename = args[0]
-
-        pidFile = nodePidfile(nodeName)
-
-        if not os.path.isfile(pidFile):
-            err("Stasher node '%s' is not running - cannot kill\n" % nodeName)
-            sys.exit(1)
-
-        pid = int(file(pidFile, "rb").read())
-        try:
-            killproc(pid)
-            print "Killed stasher node (pid %s)" % pid
-        except:
-            print "Failed to kill node (pid %s)" % pid
-        os.unlink(pidFile)
-        sys.exit(0)
-
-    try:
-        client = KNodeClient()
-    except:
-        traceback.print_exc()
-        err("Node doesn't seem to be up, or reachable on %s" % clientAddr)
-        return
-
-
-    if cmd == 'hello':
-        err("Node seems fine")
-        sys.exit(0)
-    
-    elif cmd == 'get':
-        if argc not in [1, 2]:
-            err("get: bad argument count")
-            usage()
-
-        key = args[0]
-
-        if argc == 2:
-            # try to open output file
-            path = args[1]
-            try:
-                outfile = file(path, "wb")
-            except:
-                err("Cannot open output file %s" % repr(path))
-                usage(0, 1)
-        else:
-            outfile = sys.stdout
-
-        if logVerbosity >= 3:
-            sys.stderr.write("Searching for key - may take up to %s seconds or more\n" % (
-                timeout['findData']))
-        res = client.get(key, local=localOnly)
-        if res == None:
-            err("Failed to retrieve '%s'" % key)
-            sys.exit(1)
-        else:
-            outfile.write(res)
-            outfile.flush()
-            outfile.close()
-            sys.exit(0)
-
-    elif cmd == 'put':
-        if argc not in [1, 2]:
-            err("put: bad argument count")
-            usage()
-
-        key = args[0]
-
-        if argc == 2:
-            # try to open input file
-            path = args[1]
-            try:
-                infile = file(path, "rb")
-            except:
-                err("Cannot open input file %s" % repr(path))
-                usage(0, 1)
-        else:
-            infile = sys.stdin
-
-        val = infile.read()
-        if len(val) > maxValueSize:
-            err("File is too big - please trim to %s" % maxValueSize)
-
-        if logVerbosity >= 3:
-            sys.stderr.write("Inserting key - may take up to %s seconds\n" % (
-                timeout['findNode'] + timeout['store']))
-        res = client.put(key, val, local=localOnly)
-        if res == None:
-            err("Failed to insert '%s'" % key)
-            sys.exit(1)
-        else:
-            sys.exit(0)
-
-    elif cmd == 'addref':
-        if argc not in [0, 1]:
-            err("addref: bad argument count")
-            usage()
-
-        if argc == 1:
-            # try to open input file
-            path = args[0]
-            try:
-                infile = file(path, "rb")
-            except:
-                err("Cannot open input file %s" % repr(path))
-                usage(0, 1)
-        else:
-            infile = sys.stdin
-
-        ref = infile.read()
-
-        res = client.addref(ref)
-        if res == None:
-            err("Failed to add ref")
-            sys.exit(1)
-        else:
-            sys.exit(0)
-
-    elif cmd == 'getref':
-        if argc not in [0, 1]:
-            err("getref: bad argument count")
-            usage()
-
-        res = client.getref()
-
-        if argc == 1:
-            # try to open output file
-            path = args[0]
-            try:
-                outfile = file(path, "wb")
-            except:
-                err("Cannot open output file %s" % repr(path))
-                usage(0, 1)
-        else:
-            outfile = sys.stdout
-
-        if res == None:
-            err("Failed to retrieve node ref")
-            sys.exit(1)
-        else:
-            outfile.write(res)
-            outfile.flush()
-            outfile.close()
-            sys.exit(0)
-
-    elif cmd == 'pingall':
-        if logVerbosity > 2:
-            print "Pinging all peers, waiting %s seconds for results" % timeout['ping']
-        res = client.pingall()
-        print res
-        sys.exit(0)
-
-#@-node:main
-#@-others
-#@-node:funcs
-#@+node:MAINLINE
-#@+others
-#@+node:mainline
-if __name__ == '__main__':
-
-    main()
-
-#@-node:mainline
-#@-others
-#@-node:MAINLINE
-#@-others
-
-#@-node:@file stasher.py
-#@-leo
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
index 73e7253ac0eb22ee272016a2f2f3b2b8e766e4b4..184e4a5457e9e7ac6c0185cba8d2c07727ea781b 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
@@ -121,6 +121,7 @@ public class Connection {
         _resetSentOn = -1;
         _isInbound = false;
         _connectionEvent = new ConEvent();
+	_hardDisconnected = false;
         _context.statManager().createRateStat("stream.con.windowSizeAtCongestion", "How large was our send window when we send a dup?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
         _context.statManager().createRateStat("stream.chokeSizeBegin", "How many messages were outstanding when we started to choke?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
         _context.statManager().createRateStat("stream.chokeSizeEnd", "How many messages were outstanding when we stopped being choked?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
@@ -148,7 +149,7 @@ public class Connection {
      * @return true if the packet should be sent
      */
     boolean packetSendChoke(long timeoutMs) {
-        if (false) return true;
+        // if (false) return true; // <--- what the fuck??
         long start = _context.clock().now();
         long writeExpire = start + timeoutMs;
         boolean started = false;
@@ -162,9 +163,9 @@ public class Connection {
                 
                 // no need to wait until the other side has ACKed us before sending the first few wsize
                 // packets through
-                //    if (!_connected)
-                //        return false;
-                
+		// Incorrect assumption, the constructor defaults _connected to true --Sponge
+                    if (!_connected)
+                       return false;
                 started = true;
                 if ( (_outboundPackets.size() >= _options.getWindowSize()) || (_activeResends > 0) ||
                      (_lastSendId - _highestAckedThrough > _options.getWindowSize()) ) {
@@ -180,12 +181,12 @@ public class Connection {
                         if (_log.shouldLog(Log.DEBUG))
                             _log.debug("Outbound window is full (" + _outboundPackets.size() + "/" + _options.getWindowSize() + "/" 
                                        + _activeResends + "), waiting " + timeLeft);
-                        try { _outboundPackets.wait(timeLeft); } catch (InterruptedException ie) {}
+                        try { _outboundPackets.wait(Math.min(timeLeft,250l)); } catch (InterruptedException ie) {}
                     } else {
                         if (_log.shouldLog(Log.DEBUG))
                             _log.debug("Outbound window is full (" + _outboundPackets.size() + "/" + _activeResends 
                                        + "), waiting indefinitely");
-                        try { _outboundPackets.wait(10*1000); } catch (InterruptedException ie) {}
+                        try { _outboundPackets.wait(250); } catch (InterruptedException ie) {} //10*1000
                     }
                 } else {
                     _context.statManager().addRateData("stream.chokeSizeEnd", _outboundPackets.size(), _context.clock().now() - start);
@@ -487,7 +488,6 @@ public class Connection {
         synchronized (_connectLock) { _connectLock.notifyAll(); }
         if (_log.shouldLog(Log.DEBUG))
             _log.debug("Disconnecting " + toString(), new Exception("discon"));
-        
         if (!cleanDisconnect) {
             _hardDisconnected = true;
             if (_log.shouldLog(Log.WARN))
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/MessageOutputStream.java b/apps/streaming/java/src/net/i2p/client/streaming/MessageOutputStream.java
index 4a19d5e09e600775c9b5debb8ac57b341ae92a48..3819f89997adbc00ce8785aa32621fbf01b173ef 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/MessageOutputStream.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/MessageOutputStream.java
@@ -97,6 +97,7 @@ public class MessageOutputStream extends OutputStream {
         long begin = _context.clock().now();
         while (remaining > 0) {
             WriteStatus ws = null;
+            if (_closed) throw new IOException("closed underneath us");
             // we do any waiting outside the synchronized() block because we
             // want to allow other threads to flushAvailable() whenever they want.  
             // this is the only method that *adds* to the _buf, and all 
diff --git a/apps/syndie/doc/intro.sml b/apps/syndie/doc/intro.sml
deleted file mode 100644
index 45f6c8f89ba8c713e65465bc2d62f1baec5c4c38..0000000000000000000000000000000000000000
--- a/apps/syndie/doc/intro.sml
+++ /dev/null
@@ -1,31 +0,0 @@
-Syndie is a new effort to build a user friendly secure blogging tool, exploiting the capabilities offered by anonymity and security systems such as [link schema="web" location="http://www.i2p.net/"]I2P[/link], [link schema="web" location="http://tor.eff.org/"]TOR[/link], [link schema="web" location="http://www.freenetproject.org/"]Freenet[/link], [link schema="web" location="http://www.mnetproject.org/"]MNet[/link], and others.  Abstracting away the content distribution side, Syndie allows people to [b]build content and communities[/b] that span technologies rather than tying oneself down to the ups and downs of any particular network.
-
-[cut][/cut]Syndie is working to take the technologies of the security, anonymity, and cryptography worlds and merge them with the simplicity and user focus of the blogging world.  From the user's standpoint, you could perhaps view Syndie as a distributed [link schema="web" location="http://www.livejournal.com"]LiveJournal[/link], while technically Syndie is much, much simpler.
-
-[b]How Syndie works[/b][hr][/hr]The [i]magic[/i] behind Syndie's abstraction is to ignore any content distribution issues and merely assume data moves around as necessary.  Each Syndie instance runs agains the filesystem, verifying and indexing blogs and offering up what it knows to the user through a web interface.  The core idea in Syndie, therefore, is the [b]archive[/b]- a collection of blogs categorized and ready for consumption.  
-
-Whenever someone reads or posts to a Syndie instance, it is working with the [b]local archive[/b].  However, as Syndie's development progresses, people will be able to read [b]remote archives[/b] - pulling the archive summary from an I2P [i]eepsite[/i], TOR [i]hosted service[/i], Freenet [i]Freesite[/i], MNet [i]key[/i], or (with a little less glamor) usenet, filesharing apps, or the web.  The first thing Syndie needs to use a remote archive is the archive's index - a plain text file summarizing what the archive contains ([attachment id="0"]an example[/attachment]).  From that, Syndie will let the user browse through the blogs, pulling the individual blog posts into the local archive when necessary.
-
-[b]Posting[/b][hr][/hr]Creating and posting to blogs with Syndie is trivial - simply log in to Syndie, click on the [i]Post[/i] button, and fill out the form offered.  Syndie handles all of the encryption and formatting details - packaging up the post with any attached files into a single signed, compressed, and potentially encrypted bundle, storing it in the local archive and capable of being shared with other Syndie users.  Every blog is identified by its public key behind the scenes, so there is no need for a central authority to require that your blogs are all named uniquely or any other such thing.
-
-While each blog is run by a single author, they can in turn allow other authors to post to the blog while still letting readers know that the post is authorized (though created by a different author).  Of course, if multiple people wanted to run a single blog and make it look like only one person wrote it, they could share the blog's private keys.
-
-[b]Tags[/b][hr][/hr]Following the lessons from the last few years, every Syndie entry has any number of tags associated with it by the author, allowing trivial categorization and filtering.  
-
-[b]Hosting[/b][hr][/hr]While in many scenarios it is best for people to run Syndie locally on their machine, Syndie is a fully multiuser system so anyone can be a Syndie hosting provider by simply exposing the web interface to the public.  The Syndie host's operator can password protect the blog registration interface so only authorized people can create a blog, and the operator can technically go through and delete blog posts or even entire blogs from their local archive.  A public Syndie host can be a general purpose blog repository, letting anyone sign up (following the blogger and geocities path), be a more community oriented blog repository, requiring people to introduce you to the host to sign up (following the livejournal/orkut path), be a more focused blog repository, requiring posts to stay within certain guidelines (following the indymedia path), or even to fit specialized needs by picking and choosing among the best blogs and posts out there, offering the operator's editorial flare into a comprehensive collection.
-
-[b]Syndication[/b][hr][/hr]By itself, Syndie is a nice blogging community system, but its real strength as a tool for individual and community empowerment comes when blogs are shared.  While Syndie does not aim to be a content distribution network, it does want to exploit them to allow those who require their message to get out to do so.  By design, syndicating Syndie can be done with some of the most basic tools - simply pass around the self authenticating files written to the archive and you're done.  The archive itself is organized so that you can expose it as an indexed directory in some webserver and let people wget against it, picking to pull individual posts, all posts within a blog, all posts since a given date, or all posts in all blogs.  With a very small shell script, you could parse the plain text archive summary to pull posts by size and tag as well.  People could offer up their archives as rsync repositories or package up tarballs/zipfiles of blogs or entries - simply grabbing them and extracting them into your local Syndie archive would instantly give you access to all of the content therein.
-
-Of course, manual syndication as described above has... limits.  When appropriate, Syndie will tie in to content syndication systems such as [link schema="eep" location="http://feedspace.i2p/"]Feedspace[/link] (or even good ol' Usenet) to automatically import (and export) posts.  Integration with content distribution networks like Freenet and MNet will allow the user to periodically grab a published archive index and pull down blogs as necessary.  Posting archives and blogs to those networks will be done trivially as well, though they do still depend upon a polling paradigm.
-
-[b]SML[/b][hr][/hr]Syndie is meant to work securely with any browser regardless of the browser's security.  Blog entries are written in [b]SML[/b] [i](Syndie or Secure Markup Language)[/i] with a bbcode-linke syntax, extended to exploit some of Syndie's capabilities and context.  In addition to the SML content in a blog entry, there can be any number of attachments, references to other blogs/posts/tags, nym<->public key mappings (useful for I2P host distribution), references to archives of blogs (on eepsites, freesites, etc), links to various resources, and more.  
-
-[b]Future[/b][hr][/hr]Down the road, there are lots of things to improve with Syndie.  The interface, of course, is critical, as are tools for SML authoring and improvements to SML itself to offer a more engaging user experience.  Integration with a search engine like Lucene would allow full text search through entire archives, and Atom/RSS interfaces would allow trivial import and export to existing clients.  Even further, blogs could be transparently encrypted, allowing only authorized users (those with the key) to read entries posted to them (or even know what attachments are included).  Integration with existing blogging services (such as [link schema="web" location="http://www.anonyblog.com"]anonyblog[/link], [link schema="web" location="http://blo.gs"]blo.gs[/link], and [link schema="web" location="http://livejournal.com"]livejournal[/link]) may also be explored.  Of course, bundling with I2P and other anonymity, security, and community systems will be pursued.
-
-[b]Who/where/when/why[/b][hr][/hr]The base Syndie system was written in a few days by [blog name="jrandom" bloghash="ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=" archive0="eep://dev.i2p/~jrandom" archive1="http://dev.i2p.net/~jrandom" archive2="mailto://jrandom@i2p.net"][/blog], though comes out of discussions with [link schema="eep" location="http://frosk.i2p"]Frosk[/link] and many others in the I2P community.  Yes, this is an incarnation of [b]MyI2P[/b] (or for those who remember jrand0m's flog, [b]Flogger[/b]).
-
-All of the Syndie code is of course open source and released into the public domain (the [i]real[/i] "free as in freedom"), though it does use some BSD licensed cryptographic routines and an Apache licensed file upload component.  Contributions of code are very much welcome - the source is located within the [link schema="web" location="http://www.i2p.net/cvs"]I2P codebase[/link].  Of course, those who cannot or choose not to contribute code are encouraged to [b]use[/b] Syndie - create a blog, create some content, read some content!  For those who really want to though, financial contributions to the Syndie development effort can be channeled through the [link schema="web" location="http://www.i2p.net/donate"]I2P fund[/link] (donations for Syndie are distributed to Syndie developers from time to time).
-
-The "why" of Syndie is a much bigger question, though is hopefully self-evident.  We need kickass anonymity-aware client applications so that we can get better anonymity (since without kickass clients, we don't have many users).  We also need kickass tools for safe blogging, since there are limits to the strength offered by low latency anonymity systems like I2P and TOR - Syndie goes beyond them to offer an interface to mid and high latency anonymous systems while exploiting their capabilities for fast and efficient syndication.
-
-Oh, and jrandom also lost his blog's private key, so needed something to blog with again.
diff --git a/apps/syndie/doc/readme-standalone.txt b/apps/syndie/doc/readme-standalone.txt
deleted file mode 100644
index 534612e4d2c86452880577d3450218e8689d753d..0000000000000000000000000000000000000000
--- a/apps/syndie/doc/readme-standalone.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-To run Syndie, fire it up with "java -jar launch-syndie.jar" (or, on windows,
-to run without the dos box, "javaw -jar launch-syndie.jar").  For further
-information, swing to http://localhost:8001/
\ No newline at end of file
diff --git a/apps/syndie/doc/readme.txt b/apps/syndie/doc/readme.txt
deleted file mode 100644
index 9472dae5d43172c8fef351678139033361fe68ff..0000000000000000000000000000000000000000
--- a/apps/syndie/doc/readme.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-To install this base instance:
-
-	mkdir lib
-	cp ../lib/i2p.jar lib/
-	cp ../lib/commons-el.jar lib/
-	cp ../lib/commons-logging.jar lib/
-	cp ../lib/jasper-compiler.jar lib/
-	cp ../lib/jasper-runtime.jar lib/
-	cp ../lib/javax.servlet.jar lib/
-	cp ../lib/jbigi.jar lib/
-	cp ../lib/org.mortbay.jetty.jar lib/
-	cp ../lib/xercesImpl.jar lib/
-
-To run it:
-	sh run.sh
-	firefox http://localhost:7653/syndie/
-
-You can share your archive at http://localhost:7653/ so
-that people can syndicate off you via
- cd archive ; wget -m -nH http://yourmachine:7653/
-
-You may want to add a password on the registration form 
-so that you have control over who can create blogs via /syndie/.
-To do so, set the password in the run.sh script.
-
-Windows users:
-write your own instructions.  We're alpha, here ;)
diff --git a/apps/syndie/doc/sml.sml b/apps/syndie/doc/sml.sml
deleted file mode 100644
index 327d321fc64281e08b05b0c2959b0f22fcf548a5..0000000000000000000000000000000000000000
--- a/apps/syndie/doc/sml.sml
+++ /dev/null
@@ -1,41 +0,0 @@
-[cut]A brief glance at SML[/cut]
-[b]General rules[/b]
-
-Newlines are newlines are newlines.  If you include a newline in your SML, you'll get a newline in the rendered HTML.
-
-All < and > characters are replaced by their HTML entity counterparts.
-
-All SML tags are enclosed with [[ and ]] (e.g. [[b]]bold stuff[[/b]]). ([[ and ]] characters are quoted by [[[[ and ]]]], respectively)
-
-Nesting SML tags is [b]not[/b] currently supported (though will be at a later date).
-
-All SML tags must have a beginning and end tag (even for ones without any 'body', such as [[hr]][[/hr]]).  This restriction may be removed later.
-
-Simple formatting tags behave as expected:  [[b]], [[i]], [[u]], [[h1]] through [[h5]], [[hr]], [[pre]].
-[hr][/hr]
-[b]Tag details[/b]
-
-* To cut an entry so that the summary is before while the details are afterwards:
-[[cut]]more inside...[[/cut]]
-
-* To load an attachment as an image with "syndie's logo" as the alternate text:
-[[img attachment="0"]]syndie's logo[[/img]]
-
-* To add a download link to an attachment:
-[[attachment id="0"]]anchor text[[/img]]
-
-* To quote someone:
-[[quote author="who you are quoting" location="blog://ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=/1234567890"]]stuff they said[[/quote]]
-
-* To sample some code:
-[[code location="eep://dev.i2p/cgi-bin/cvsweb.cgi/i2p/index.html"]]<html>[[/code]]
-
-* To link to a [blog name="jrandom" bloghash="ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=" blogentry="1124402137773" archive0="eep://dev.i2p/~jrandom/archive" archive1="irc2p://jrandom@irc.postman.i2p/#i2p"]bitchin' blog[/blog]: 
-[[blog name="the blogs name" bloghash="ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=" blogtag="tag" blogentry="123456789" archive0="eep://dev.i2p/~jrandom/archive/" archive1="freenet://SSK@blah/archive//"]]description of the blog[[/blog]].  blogentry and blogtag are optional and there can be any number of archiveN locations specified.
-
-* To link to an [link schema="eep" location="http://dev.i2p/"]external resource[/link]:
-[[link schema="eep" location="http://dev.i2p/"]]link to it[[/link]].  
-[i]The schema should be a network selection tool, such as "eep" for an eepsite, "tor" for a tor hidden service, "web" for a normal website, "freenet" for a freenet key, etc.  The local user's Syndie configuration should include information necessary for the user to access the content referenced through the given schemas.[/i]
-
-* To pass an [address name="dev.i2p" schema="eep" location="NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA"]addressbook entry[/address]:
-[[address name="dev.i2p" schema="eep" location="NF2...AAAA"]]add it[[/address]].
diff --git a/apps/syndie/java/build.xml b/apps/syndie/java/build.xml
deleted file mode 100644
index 7d0bdc4565e5dbda530dc9fd68fb52bab1164db7..0000000000000000000000000000000000000000
--- a/apps/syndie/java/build.xml
+++ /dev/null
@@ -1,172 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project basedir="." default="all" name="syndie">
-    <target name="all" depends="clean, build" />
-    <target name="build" depends="builddep, jar" />
-    <target name="builddep">
-        <ant dir="../../jetty/" target="build" />
-        <ant dir="../../../core/java/" target="build" />
-	<!-- ministreaming will build core -->
-    </target>
-    <target name="compile">
-        <mkdir dir="./build" />
-        <delete dir="./build/obj" />
-        <mkdir dir="./build/obj" />
-        <javac 
-            srcdir="./src"
-            debug="true" deprecation="on" source="1.3" target="1.3" 
-            destdir="./build/obj" 
-            classpath="../../../core/java/build/i2p.jar:../../jetty/jettylib/org.mortbay.jetty.jar:../../jetty/jettylib/javax.servlet.jar:../../jdom/jdom.jar:../../rome/rome-0.8.jar" />
-    </target>
-    <target name="jar" depends="builddep, compile">
-        <jar destfile="./build/syndie.jar" basedir="./build/obj" includes="**/*.class">
-            <manifest>
-                <attribute name="Main-Class" value="net.i2p.syndie.CLIPost" />
-                <attribute name="Class-Path" value="i2p.jar" />
-            </manifest>
-        </jar>
-        <jar destfile="./build/sucker.jar" basedir="./build/obj" includes="net/i2p/syndie/Sucker.class, net/i2p/syndie/SuckerFetchListener.class">
-            <manifest>
-                <attribute name="Main-Class" value="net.i2p.syndie.Sucker" />
-                <attribute name="Class-Path" value="i2p.jar jdom.jar rome-0.8.jar" />
-            </manifest>
-        </jar>
-        <ant target="war" />
-    </target>
-    <target name="war" depends="builddep, compile, precompilejsp"> 
-	<delete file="../syndie.war" />
-	<delete dir="./tmpwar" />
-        <mkdir dir="./tmpwar" />
-        <copy file="../../jdom/jdom.jar" tofile="./tmpwar/jdom.jar" />
-        <copy file="../../rome/rome-0.8.jar" tofile="./tmpwar/rome-0.8.jar" />
-
-        <war destfile="../syndie.war" webxml="../jsp/web-out.xml">
-          <fileset dir="../jsp/" includes="**/*" excludes=".nbintdb, web.xml, web-out.xml, web-fragment.xml, **/*.java, **/*.jsp" />
-          <classes dir="./build/obj" />
-          <lib dir="./tmpwar" />
-        </war>
-	<delete dir="./tmpwar" />
-    </target>
-    <target name="standalone" depends="standalone_prep">
-        <zip destfile="syndie-standalone.zip">
-          <zipfileset dir="./dist/" prefix="syndie/" />
-        </zip>
-    </target>
-    <target name="standalone_prep" depends="war">
-        <javac debug="true" deprecation="on" source="1.3" target="1.3" 
-               destdir="./build" srcdir="src/" includes="net/i2p/syndie/web/RunStandalone.java" >
-            <classpath>
-                <pathelement location="../../jetty/jettylib/commons-logging.jar" />
-                <pathelement location="../../jetty/jettylib/commons-el.jar" />
-                <pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
-                <pathelement location="../../jetty/jettylib/javax.servlet.jar" />
-                <pathelement location="../../../core/java/build/i2p.jar" />
-            </classpath>
-        </javac>
-    
-        <jar destfile="./build/launch-syndie.jar" basedir="./build/" includes="net/i2p/syndie/web/RunStandalone.class">
-            <manifest>
-                <attribute name="Main-Class" value="net.i2p.syndie.web.RunStandalone" />
-                <attribute name="Class-Path" value="lib/i2p.jar lib/commons-el.jar lib/commons-logging.jar lib/jasper-compiler.jar lib/jasper-runtime.jar lib/javax.servlet.jar lib/org.mortbay.jetty.jar" />
-            </manifest>
-        </jar>
-
-        <delete dir="./dist" />
-        <mkdir dir="./dist" />
-        <copy file="./build/launch-syndie.jar" tofile="./dist/launch-syndie.jar" />
-        <copy file="../syndie.war" tofile="./dist/syndie.war" />
-        <mkdir dir="./dist/lib" />
-        <copy file="../../../core/java/build/i2p.jar" tofile="./dist/lib/i2p.jar" />
-        <copy file="../../jetty/jettylib/commons-el.jar" tofile="./dist/lib/commons-el.jar" />
-        <copy file="../../jetty/jettylib/commons-logging.jar" tofile="./dist/lib/commons-logging.jar" />
-        <copy file="../../jetty/jettylib/javax.servlet.jar" tofile="./dist/lib/javax.servlet.jar" />
-        <copy file="../../jetty/jettylib/org.mortbay.jetty.jar" tofile="./dist/lib/org.mortbay.jetty.jar" />
-        <copy file="../../jetty/jettylib/jasper-compiler.jar" tofile="./dist/lib/jasper-compiler.jar" />
-        <copy file="../../jetty/jettylib/jasper-runtime.jar" tofile="./dist/lib/jasper-runtime.jar" />
-        <copy file="../jetty-syndie.xml" tofile="./dist/jetty-syndie.xml" />
-        <copy file="../doc/readme-standalone.txt" tofile="./dist/readme.txt" />
-        <mkdir dir="./dist/work" />
-        <mkdir dir="./dist/logs" />
-        <mkdir dir="./dist/archive" />
-
-	<copy file="../../../installer/resources/blogMeta.snm" tofile="dist/archive/ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=/meta.snm" />
-	<copy file="../../../installer/resources/blogPost.snd" tofile="dist/archive/ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=/1132012800001.snd" />
-        
-        <zip destfile="syndie-standalone.zip">
-         <zipfileset dir="./dist/" prefix="syndie/" />
-        </zip>
-    </target>
-    <target name="precompilejsp">
-        <delete dir="../jsp/WEB-INF/" />
-        <delete file="../jsp/web-fragment.xml" />
-        <delete file="../jsp/web-out.xml" />
-        <mkdir dir="../jsp/WEB-INF/" />
-        <mkdir dir="../jsp/WEB-INF/classes" />
-        <!-- there are various jspc ant tasks, but they all seem a bit flakey -->
-        <java classname="org.apache.jasper.JspC" fork="true" >
-            <classpath>
-                <pathelement location="../../jetty/jettylib/jasper-compiler.jar" />
-                <pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
-                <pathelement location="../../jetty/jettylib/javax.servlet.jar" />
-                <pathelement location="../../jetty/jettylib/commons-logging.jar" />
-                <pathelement location="../../jetty/jettylib/commons-el.jar" />
-                <pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
-                <pathelement location="../../jetty/jettylib/javax.servlet.jar" />
-                <pathelement location="../../jetty/jettylib/ant.jar" />
-                <pathelement location="../../jdom/jdom.jar" />
-                <pathelement location="../../rome/rome-0.8.jar" />
-                <pathelement location="build/obj" />
-                <pathelement location="../../../core/java/build/i2p.jar" />
-            </classpath>
-            <arg value="-d" />
-            <arg value="../jsp/WEB-INF/classes" />
-            <arg value="-p" />
-            <arg value="net.i2p.syndie.jsp" />
-            <arg value="-webinc" />
-            <arg value="../jsp/web-fragment.xml" />
-            <arg value="-webapp" />
-            <arg value="../jsp/" />
-        </java>
-        <javac debug="true" deprecation="on" source="1.3" target="1.3" 
-               destdir="../jsp/WEB-INF/classes/" srcdir="../jsp/WEB-INF/classes" includes="**/*.java" >
-            <classpath>
-                <pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
-                <pathelement location="../../jetty/jettylib/javax.servlet.jar" />
-                <pathelement location="../../jetty/jettylib/commons-logging.jar" />
-                <pathelement location="../../jetty/jettylib/commons-el.jar" />
-                <pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
-                <pathelement location="../../jetty/jettylib/javax.servlet.jar" />
-                <pathelement location="../../jdom/jdom.jar" />
-                <pathelement location="../../rome/rome-0.8.jar" />
-                <pathelement location="build/obj" />
-                <pathelement location="../../../core/java/build/i2p.jar" />
-            </classpath>
-        </javac>
-        <copy file="../jsp/web.xml" tofile="../jsp/web-out.xml" />
-        <loadfile property="jspc.web.fragment" srcfile="../jsp/web-fragment.xml" />
-        <replace file="../jsp/web-out.xml">
-            <replacefilter token="&lt;!-- precompiled servlets --&gt;" value="${jspc.web.fragment}" />
-        </replace>
-    </target>
-    <target name="javadoc">
-        <mkdir dir="./build" />
-        <mkdir dir="./build/javadoc" />
-        <javadoc 
-            sourcepath="./src:../../../core/java/src" destdir="./build/javadoc" 
-            packagenames="*" 
-            use="true" 
-            splitindex="true" 
-            windowtitle="syndie" />
-    </target>
-    <target name="clean">
-        <delete dir="./build" />
-        <delete dir="./dist" />
-        <delete dir="../jsp/WEB-INF/classes" />
-        <delete file="./syndie-standalone.zip" />
-    </target>
-    <target name="cleandep" depends="clean">
-        <ant dir="../../../core/java/" target="distclean" />
-    </target>
-    <target name="distclean" depends="clean">
-        <ant dir="../../../core/java/" target="distclean" />
-    </target>
-</project>
diff --git a/apps/syndie/java/src/net/i2p/syndie/Archive.java b/apps/syndie/java/src/net/i2p/syndie/Archive.java
deleted file mode 100644
index d44d3dfd072b52e4e13ac3c21007353cbafa0817..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/Archive.java
+++ /dev/null
@@ -1,495 +0,0 @@
-package net.i2p.syndie;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
-import net.i2p.I2PAppContext;
-import net.i2p.data.Base64;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Hash;
-import net.i2p.data.SessionKey;
-import net.i2p.syndie.data.ArchiveIndex;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.util.FileUtil;
-import net.i2p.util.Log;
-
-/**
- * Store blog info in the local filesystem.
- *
- * Entries are stored under:
- *  $rootDir/$h(blogKey)/$entryId.snd (the index lists them as YYYYMMDD_n_jKB)
- * Blog info is stored under:
- *  $rootDir/$h(blogKey)/meta.snm
- * Archive summary is stored under
- *  $rootDir/archive.txt
- * Any key=value pairs in
- *  $rootDir/archiveHeaders.txt
- * are injected into the archive.txt on regeneration.
- *
- * When entries are loaded for extraction/verification/etc, their contents are written to
- *  $cacheDir/$h(blogKey)/$entryId/ (e.g. $cacheDir/$h(blogKey)/$entryId/entry.sml)
- */
-public class Archive {
-    private I2PAppContext _context;
-    private Log _log;
-    private BlogManager _mgr;
-    private File _rootDir;
-    private File _cacheDir;
-    private Map _blogInfo;
-    private ArchiveIndex _index;
-    private EntryExtractor _extractor;
-    private String _defaultSelector;
-    
-    public static final String METADATA_FILE = "meta.snm";
-    public static final String INDEX_FILE = "archive.txt";
-    public static final String HEADER_FILE = "archiveHeaders.txt";
-
-    private static final FilenameFilter _entryFilenameFilter = new FilenameFilter() {
-        public boolean accept(File dir, String name) { return name.endsWith(".snd"); }
-    };    
-    
-    public Archive(I2PAppContext ctx, String rootDir, String cacheDir, BlogManager mgr) {
-        _context = ctx;
-        _log = ctx.logManager().getLog(Archive.class);
-        _mgr = mgr;
-        _rootDir = new File(rootDir);
-        if (!_rootDir.exists())
-            _rootDir.mkdirs();
-        _cacheDir = new File(cacheDir);
-        if (!_cacheDir.exists())
-            _cacheDir.mkdirs();
-        _blogInfo = new HashMap();
-        _index = null;
-        _extractor = new EntryExtractor(ctx);
-        _defaultSelector = ctx.getProperty("syndie.defaultSelector");
-        if (_defaultSelector == null) _defaultSelector = "";
-        reloadInfo();
-    }
-    
-    public void reloadInfo() {
-        File f[] = _rootDir.listFiles();
-        List info = new ArrayList();
-        for (int i = 0; i < f.length; i++) {
-            if (f[i].isDirectory()) {
-                File meta = new File(f[i], METADATA_FILE);
-                if (meta.exists()) {
-                    BlogInfo bi = new BlogInfo();
-                    FileInputStream fi = null;
-                    try {
-                        fi = new FileInputStream(meta);
-                        bi.load(fi);
-                        if (_mgr.isBanned(bi.getKey().calculateHash())) {
-                            fi.close();
-                            fi = null;
-                            _log.error("Deleting banned blog " + bi.getKey().calculateHash().toBase64());
-                            delete(bi.getKey().calculateHash());
-                            continue;
-                        }
-                        if (bi.verify(_context)) {
-                            info.add(bi);
-                        } else {
-                            _log.error("BlogInfo is invalid: " + bi);
-                            meta.delete();
-                        }
-                    } catch (IOException ioe) {
-                        _log.error("Error loading the blog", ioe);
-                    } finally {
-                        if (fi != null) try { fi.close(); } catch (IOException ioe) {}
-                    }
-                }
-            }
-        }
-        
-        synchronized (_blogInfo) {
-            _blogInfo.clear();
-            for (int i = 0; i < info.size(); i++) {
-                BlogInfo bi = (BlogInfo)info.get(i);
-                _blogInfo.put(bi.getKey().calculateHash(), bi);
-            }
-        }
-    }
-    
-    public String getDefaultSelector() { return _defaultSelector; }
-    public void setDefaultSelector(String sel) {
-        if (sel == null)
-            _defaultSelector = "";
-        else
-            _defaultSelector = sel;
-    }
-        
-    public BlogInfo getBlogInfo(BlogURI uri) {
-        if (uri == null) return null;
-        synchronized (_blogInfo) {
-            return (BlogInfo)_blogInfo.get(uri.getKeyHash());
-        }
-    }
-    public BlogInfo getBlogInfo(Hash key) {
-        synchronized (_blogInfo) { 
-            return (BlogInfo)_blogInfo.get(key); 
-        }
-    }
-    public boolean storeBlogInfo(BlogInfo info) { 
-        if (!info.verify(_context)) {
-            _log.warn("Not storing invalid blog " + info);
-            return false;
-        }
-        
-        if (_mgr.isBanned(info.getKey().calculateHash())) {
-            _log.error("Not storing banned blog " + info.getKey().calculateHash().toBase64(), new Exception("Stored by"));
-            return false;
-        }
-        
-        boolean isNew = true;
-        synchronized (_blogInfo) {
-            BlogInfo old = (BlogInfo)_blogInfo.get(info.getKey().calculateHash());
-            if ( (old == null) || (old.getEdition() < info.getEdition()) )
-                _blogInfo.put(info.getKey().calculateHash(), info); 
-            else
-                isNew = false;
-        }
-        if (!isNew) return true; // valid entry, but not stored, since its old
-        try {
-            File blogDir = new File(_rootDir, info.getKey().calculateHash().toBase64());
-            blogDir.mkdirs();
-            File blogFile = new File(blogDir, "meta.snm");
-            FileOutputStream out = new FileOutputStream(blogFile);
-            info.write(out);
-            out.close();
-            if (_log.shouldLog(Log.DEBUG))
-                _log.debug("Blog info written to " + blogFile.getPath());
-            return true;
-        } catch (IOException ioe) {
-            _log.error("Error writing out info", ioe);
-            return false;
-        }
-    }
-    
-    public List listBlogs() {
-        synchronized (_blogInfo) {
-            return new ArrayList(_blogInfo.values());
-        }
-    }
-    
-    private File getEntryDir(File entryFile) {
-        String name = entryFile.getName();
-        if (!name.endsWith(".snd")) throw new RuntimeException("hmm, why are we trying to get an entry dir for " + entryFile.getAbsolutePath());
-        String blog = entryFile.getParentFile().getName();
-        File blogDir = new File(_cacheDir, blog);
-        return new File(blogDir, name.substring(0, name.length()-4));
-        //return new File(entryFile.getParentFile(), "." + name.substring(0, name.length()-4));
-    }
-    
-    /**
-     * Expensive operation, reading all entries within the blog and parsing out the tags.
-     * Whenever possible, query the index instead of the archive
-     *
-     */
-    public List listTags(Hash blogKeyHash) {
-        List rv = new ArrayList();
-        BlogInfo info = getBlogInfo(blogKeyHash);
-        if (info == null)
-            return rv;
-        
-        File blogDir = new File(_rootDir, Base64.encode(blogKeyHash.getData()));
-        File entries[] = blogDir.listFiles(_entryFilenameFilter);
-        for (int j = 0; j < entries.length; j++) {
-            try {
-                File entryDir = getEntryDir(entries[j]);
-                EntryContainer entry = null;
-                if (entryDir.exists())
-                    entry = getCachedEntry(entryDir);
-                if ( (entry == null) || (!entryDir.exists()) ) {
-                    if (!extractEntry(entries[j], entryDir, info)) {
-                        if (_log.shouldLog(Log.ERROR))
-                            _log.error("Entry " + entries[j].getPath() + " is not valid");
-                        entries[j].delete();
-                        continue;
-                    }
-                    entry = getCachedEntry(entryDir);
-                }
-                String tags[] = entry.getTags();
-                for (int t = 0; t < tags.length; t++) {
-                    if (!rv.contains(tags[t])) {
-                        if (_log.shouldLog(Log.DEBUG))
-                            _log.debug("Found a new tag in cached " + entry.getURI() + ": " + tags[t]);
-                        rv.add(tags[t]);
-                    }
-                }
-            } catch (IOException ioe) {
-                _log.error("Error listing tags", ioe);
-            }
-        } // end iterating over the entries 
-        
-        return rv;
-    }
-
-    /**
-     * Extract the entry to the given dir, returning true if it was verified properly
-     *
-     */
-    private boolean extractEntry(File entryFile, File entryDir, BlogInfo info) throws IOException {
-        if (!entryDir.exists())
-            entryDir.mkdirs();
-        
-        boolean ok = true;
-        try {
-            ok = _extractor.extract(entryFile, entryDir, null, info);
-        } catch (IOException ioe) {
-            ok = false;
-            _log.error("Error extracting " + entryFile.getPath() + ", deleting it", ioe);
-        }
-        if (!ok) {
-            File files[] = entryDir.listFiles();
-            for (int i = 0; i < files.length; i++)
-                files[i].delete();
-            entryDir.delete();
-        }
-        return ok;
-    }
-    
-    private EntryContainer getCachedEntry(File entryDir) {
-        try {
-            CachedEntry ce = new CachedEntry(entryDir);
-            if (ce.isValid())
-                return ce;
-            return null;
-        } catch (IOException ioe) {
-            _log.warn("Error reading cached entry... deleting cache elements");
-        }
-        
-        File files[] = entryDir.listFiles();
-        for (int i = 0; i < files.length; i++)
-            files[i].delete();
-        entryDir.delete();
-        return null;
-    }
-    
-    public EntryContainer getEntry(BlogURI uri) { return getEntry(uri, null); }
-    public EntryContainer getEntry(BlogURI uri, SessionKey blogKey) {
-        List entries = listEntries(uri, null, blogKey);
-        if (entries.size() > 0)
-            return (EntryContainer)entries.get(0);
-        else
-            return null;
-    }
-    
-    public List listEntries(BlogURI uri, String tag, SessionKey blogKey) {
-        if (uri == null) return new ArrayList();
-        return listEntries(uri.getKeyHash(), uri.getEntryId(), tag, blogKey);
-    }
-    public List listEntries(Hash blog, long entryId, String tag, SessionKey blogKey) { 
-        List rv = new ArrayList();
-        BlogInfo info = getBlogInfo(blog);
-        if (info == null)
-            return rv;
-        
-        File blogDir = new File(_rootDir, blog.toBase64());
-        File entries[] = blogDir.listFiles(_entryFilenameFilter);
-        if (entries == null)
-            return rv;
-        for (int i = 0; i < entries.length; i++) {
-            try {
-                EntryContainer entry = null;
-                if (blogKey == null) {
-                    // no key, cache.
-                    File entryDir = getEntryDir(entries[i]);
-                    if (entryDir.exists()) {
-                        entry = getCachedEntry(entryDir);
-                    }
-                    if ((entry == null) || !entryDir.exists()) {
-                        if (!extractEntry(entries[i], entryDir, info)) {
-                            _log.error("Entry " + entries[i].getPath() + " is not valid");
-                            entries[i].delete();
-                            continue;
-                        }
-                        entry = getCachedEntry(entryDir);
-                    }
-                } else {
-                    // we have an explicit key - no caching
-                    entry = new EntryContainer();
-                    FileInputStream fi = null;
-                    try {
-                        fi = new FileInputStream(entries[i]);
-                        entry.load(fi);
-                    } finally {
-                        if (fi != null) try { fi.close(); } catch (IOException ioe) {}
-                    }
-                    boolean ok = entry.verifySignature(_context, info);
-                    if (!ok) {
-                        _log.error("Keyed entry " + entries[i].getPath() + " is not valid");
-                        entries[i].delete();
-                        continue;
-                    }
-
-                    entry.parseRawData(_context, blogKey);
-                
-                    entry.setCompleteSize((int)entries[i].length());
-                }
-                
-                if (entryId >= 0) {
-                    if (entry.getURI().getEntryId() == entryId) {
-                        rv.add(entry);
-                        return rv;
-                    }
-                } else if (tag != null) {
-                    String tags[] = entry.getTags();
-                    for (int j = 0; j < tags.length; j++) {
-                        if (tags[j].equals(tag)) {
-                            rv.add(entry);
-                            if (_log.shouldLog(Log.DEBUG))
-                                _log.debug("cached entry matched requested tag [" + tag + "]: " + entry.getURI());
-                            break;
-                        }
-                    }
-                } else {
-                    if (_log.shouldLog(Log.DEBUG))
-                        _log.debug("cached entry is ok and no id or tag was requested: " + entry.getURI());
-                    rv.add(entry);
-                }
-            } catch (IOException ioe) {
-                _log.error("Error listing entries", ioe);
-            }
-        }
-        return rv;
-    }
-    
-    public synchronized void delete(Hash blog) {
-        if (blog == null) return;
-        File blogDir = new File(_rootDir, blog.toBase64());
-        boolean deleted = FileUtil.rmdir(blogDir, false);
-        File cacheDir = new File(_cacheDir, blog.toBase64());
-        deleted = FileUtil.rmdir(cacheDir, false) && deleted;
-        _log.info("Deleted blog " + blog.toBase64() + " completely? " + deleted);
-    }
-    
-    public boolean storeEntry(EntryContainer container) {
-        if (container == null) return false;
-        BlogURI uri = container.getURI();
-        if (uri == null) return false;
-
-        File blogDir = new File(_rootDir, uri.getKeyHash().toBase64());
-        blogDir.mkdirs();
-        File entryFile = new File(blogDir, getEntryFilename(uri.getEntryId()));
-        if (entryFile.exists()) return true;
-
-
-        BlogInfo info = getBlogInfo(uri);
-        if (info == null) {
-            _log.error("no blog metadata for the uri " + uri);
-            return false;
-        }
-        if (!container.verifySignature(_context, info)) {
-            _log.error("Not storing the invalid blog entry at " + uri);
-            return false;
-        } else {
-            //System.out.println("Signature is valid: " + container.getSignature() + " for info " + info);
-        }
-        try {
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();   
-            container.write(baos, true);
-            byte data[] = baos.toByteArray();
-            FileOutputStream out = new FileOutputStream(entryFile);
-            out.write(data);
-            out.close();
-            container.setCompleteSize(data.length);
-            return true;
-        } catch (IOException ioe) {
-            _log.error("Error storing", ioe);
-            return false;
-        }
-    }
-    
-    public static String getEntryFilename(long entryId) { return entryId + ".snd"; }
-    
-    private static SimpleDateFormat _dateFmt = new SimpleDateFormat("yyyyMMdd", Locale.UK);
-    public static String getIndexName(long entryId, int numBytes) {
-        try {
-            synchronized (_dateFmt) {
-                String yy = _dateFmt.format(new Date(entryId));
-                long begin = _dateFmt.parse(yy).getTime();
-                long n = entryId - begin;
-                int kb = numBytes / 1024;
-                return yy + '_' + n + '_' + kb + "KB";
-            }
-        } catch (NumberFormatException nfe) {
-            nfe.printStackTrace();
-            return "UNKNOWN";
-        } catch (ParseException pe) {
-            pe.printStackTrace();
-            return "UNKNOWN";
-        }
-    }
-
-    public static long getEntryIdFromIndexName(String entryIndexName) {
-        if (entryIndexName == null) return -1;
-        if (entryIndexName.endsWith(".snd"))
-            entryIndexName = entryIndexName.substring(0, entryIndexName.length() - 4);
-        int endYY = entryIndexName.indexOf('_');
-        if (endYY <= 0) return -1;
-        int endN = entryIndexName.indexOf('_', endYY+1);
-        if (endN <= 0) return -1;
-        String yy = entryIndexName.substring(0, endYY);
-        String n = entryIndexName.substring(endYY+1, endN);
-        try {
-            synchronized (_dateFmt) {
-                long dayBegin = _dateFmt.parse(yy).getTime();
-                long dayEntry = Long.parseLong(n);
-                return dayBegin + dayEntry;
-            }
-        } catch (NumberFormatException nfe) {
-            nfe.printStackTrace();
-        } catch (ParseException pe) {
-            pe.printStackTrace();
-        }
-        return -1;
-    }
-    public static int getSizeFromIndexName(String entryIndexName) {
-        if (entryIndexName == null) return -1;
-        if (entryIndexName.endsWith(".snd"))
-            entryIndexName = entryIndexName.substring(0, entryIndexName.length() - 4);
-        int beginSize = entryIndexName.lastIndexOf('_');
-        if ( (beginSize <= 0) || (beginSize >= entryIndexName.length()-3) )
-            return -1;
-        try {
-            String sz = entryIndexName.substring(beginSize+1, entryIndexName.length()-2);
-            return Integer.parseInt(sz);
-        } catch (NumberFormatException nfe) {
-            nfe.printStackTrace();
-        } 
-        return -1;
-    }
-    
-    public ArchiveIndex getIndex() { 
-        if (_index == null)
-            regenerateIndex();
-        return _index; 
-    }
-
-    public File getArchiveDir() { return _rootDir; }
-    public File getIndexFile() { return new File(_rootDir, INDEX_FILE); }
-    public void regenerateIndex() {
-        reloadInfo();
-        _index = ArchiveIndexer.index(_context, this);
-        try {
-            FileOutputStream out = new FileOutputStream(new File(_rootDir, INDEX_FILE));
-            out.write(DataHelper.getUTF8(_index.toString()));
-            out.flush();
-        } catch (IOException ioe) {
-            _log.error("Error writing out the index");
-        }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/ArchiveIndexer.java b/apps/syndie/java/src/net/i2p/syndie/ArchiveIndexer.java
deleted file mode 100644
index 9a39f70a0330ee3ebf5aadcada928d0aee79ff14..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/ArchiveIndexer.java
+++ /dev/null
@@ -1,228 +0,0 @@
-package net.i2p.syndie;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.StringTokenizer;
-import java.util.TreeMap;
-
-import net.i2p.I2PAppContext;
-import net.i2p.data.Base64;
-import net.i2p.data.Hash;
-import net.i2p.syndie.data.ArchiveIndex;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.syndie.data.LocalArchiveIndex;
-import net.i2p.syndie.sml.HTMLRenderer;
-import net.i2p.syndie.sml.SMLParser;
-import net.i2p.util.Log;
-
-/**
- * Dig through the archive to build an index
- */
-class ArchiveIndexer {
-    private static final int RECENT_BLOG_COUNT = 10;
-    private static final int RECENT_ENTRY_COUNT = 10;
-    
-    public static ArchiveIndex index(I2PAppContext ctx, Archive source) {
-        Log log = ctx.logManager().getLog(ArchiveIndexer.class);
-        LocalArchiveIndex rv = new LocalArchiveIndex(ctx);
-        WritableThreadIndex threads = new WritableThreadIndex();
-        rv.setGeneratedOn(ctx.clock().now());
-        
-        File rootDir = source.getArchiveDir();
-      
-        File headerFile = new File(rootDir, Archive.HEADER_FILE);
-        if (headerFile.exists()) {
-            BufferedReader in = null;
-            try {
-                 in = new BufferedReader(new InputStreamReader(new FileInputStream(headerFile), "UTF-8"));
-                String line = null;
-                while ( (line = in.readLine()) != null) {
-                    StringTokenizer tok = new StringTokenizer(line, ":");
-                    if (tok.countTokens() == 2)
-                        rv.setHeader(tok.nextToken(), tok.nextToken());
-                }
-            } catch (IOException ioe) {
-                log.error("Error reading header file", ioe);
-            } finally {
-                if (in != null) try { in.close(); } catch (IOException ioe) {}
-            }
-        }
-        
-        // things are new if we just received them in the last day
-        long newSince = ctx.clock().now() - 24*60*60*1000;
-        
-        rv.setVersion(Version.INDEX_VERSION);
-        
-        /** 0-lowestEntryId --> blog Hash */
-        Map blogsByAge = new TreeMap();
-        /** 0-entryId --> BlogURI */
-        Map entriesByAge = new TreeMap();
-        List blogs = source.listBlogs();
-        rv.setAllBlogs(blogs.size());
-        
-        int newEntries = 0;
-        int allEntries = 0;
-        long newSize = 0;
-        long totalSize = 0;
-        int newBlogs = 0;
-        
-        SMLParser parser = new SMLParser(ctx);
-        
-        for (int i = 0; i < blogs.size(); i++) {
-            BlogInfo cur = (BlogInfo)blogs.get(i);
-            Hash key = cur.getKey().calculateHash();
-            String keyStr = Base64.encode(key.getData());
-            File blogDir = new File(rootDir, Base64.encode(key.getData()));
-
-            File metaFile = new File(blogDir, Archive.METADATA_FILE);
-            long metadate = metaFile.lastModified();
-
-            List entries = source.listEntries(key, -1, null, null);
-            if (log.shouldLog(Log.DEBUG))
-                log.debug("Entries under " + key + ": " + entries);
-            /** tag name --> ordered map of entryId to EntryContainer */
-            Map tags = new TreeMap();
-            
-            for (int j = 0; j < entries.size(); j++) {
-                EntryContainer entry = (EntryContainer)entries.get(j);
-                entriesByAge.put(new Long(0-entry.getURI().getEntryId()), entry.getURI());
-                allEntries++;
-                totalSize += entry.getCompleteSize();
-                String entryTags[] = entry.getTags();
-                threads.addEntry(entry.getURI(), entryTags);
-                log.debug("Adding entry " + entry.getURI() + " to the threads, with tag count " + (entryTags != null ? entryTags.length : 0));
-                for (int t = 0; t < entryTags.length; t++) {
-                    if (!tags.containsKey(entryTags[t])) {
-                        tags.put(entryTags[t], new TreeMap());
-                        //System.err.println("New tag [" + entryTags[t] + "]");
-                    }
-                    Map entriesByTag = (Map)tags.get(entryTags[t]);
-                    entriesByTag.put(new Long(0-entry.getURI().getEntryId()), entry);
-                    if (log.shouldLog(Log.DEBUG))
-                        log.debug("Entries under tag " + entryTags[t] + ":" + entriesByTag.values());
-                }
-                    
-                if (entry.getURI().getEntryId() >= newSince) {
-                    newEntries++;
-                    newSize += entry.getCompleteSize();
-                }
-                HeaderReceiver rec = new HeaderReceiver();
-                parser.parse(entry.getEntry().getText(), rec);
-                String reply = rec.getHeader(HTMLRenderer.HEADER_IN_REPLY_TO);
-                if (reply != null) {
-                    String forceNewThread = rec.getHeader(HTMLRenderer.HEADER_FORCE_NEW_THREAD);
-                    if ( (forceNewThread != null) && (Boolean.valueOf(forceNewThread).booleanValue()) ) {
-                        // ignore the parent
-                        log.warn("Ignore the parent of " + entry.getURI() + ": " + reply);
-                    } else {
-                        BlogURI parent = new BlogURI(reply.trim());
-                        if ( (parent.getKeyHash() != null) && (parent.getEntryId() >= 0) ) {
-                            rv.addReply(parent, entry.getURI());
-                            threads.addParent(parent, entry.getURI());
-                        } else if (log.shouldLog(Log.WARN)) {
-                            log.warn("Parent of " + entry.getURI() + " is not valid: [" + reply.trim() + "]");
-                        }
-                    }
-                }
-            }
-            
-            long lowestEntryId = -1;
-            for (Iterator iter = tags.keySet().iterator(); iter.hasNext(); ) {
-                String tagName = (String)iter.next();
-                Map tagEntries = (Map)tags.get(tagName);
-                long highestId = -1;
-                if (tagEntries.size() <= 0) break;
-                Long id = (Long)tagEntries.keySet().iterator().next();
-                highestId = 0 - id.longValue();
-                
-                rv.addBlog(key, tagName, highestId);
-                for (Iterator entryIter = tagEntries.values().iterator(); entryIter.hasNext(); ) {
-                    EntryContainer entry = (EntryContainer)entryIter.next();
-                    String indexName = Archive.getIndexName(entry.getURI().getEntryId(), entry.getCompleteSize());
-                    rv.addBlogEntry(key, tagName, indexName);
-                    if (!entryIter.hasNext())
-                        lowestEntryId = entry.getURI().getEntryId();
-                }
-            }
-            
-            if (lowestEntryId > newSince)
-                newBlogs++;
-            
-            blogsByAge.put(new Long(0-lowestEntryId), key);
-        }
-        
-        rv.setAllEntries(allEntries);
-        rv.setNewBlogs(newBlogs);
-        rv.setNewEntries(newEntries);
-        rv.setTotalSize(totalSize);
-        rv.setNewSize(newSize);
-
-        int i = 0;
-        for (Iterator iter = blogsByAge.keySet().iterator(); iter.hasNext() && i < RECENT_BLOG_COUNT; i++) {
-            Long when = (Long)iter.next();
-            Hash key = (Hash)blogsByAge.get(when);
-            rv.addNewestBlog(key);
-        }
-        i = 0;
-        for (Iterator iter = entriesByAge.keySet().iterator(); iter.hasNext() && i < RECENT_ENTRY_COUNT; i++) {
-            Long when = (Long)iter.next();
-            BlogURI uri = (BlogURI)entriesByAge.get(when);
-            rv.addNewestEntry(uri);
-        }
-        
-        threads.organizeTree();
-        if (log.shouldLog(Log.DEBUG))
-            log.debug("Tree: \n" + threads.toString());
-        rv.setThreadedIndex(threads);
-        
-        return rv;
-    }
-    
-    private static class HeaderReceiver implements SMLParser.EventReceiver {
-        private Properties _headers;
-        public HeaderReceiver() { _headers = null; }
-        public String getHeader(String name) { return (_headers != null ? _headers.getProperty(name) : null); }
-        public void receiveHeader(String header, String value) { 
-            if (_headers == null) _headers = new Properties();
-            _headers.setProperty(header, value);
-        }
-        
-        public void receiveAddress(String name, String schema, String protocol, String location, String anchorText) {}
-        public void receiveArchive(String name, String description, String locationSchema, String location, String postingKey, String anchorText) {}
-        public void receiveAttachment(int id, int thumbnail, String anchorText) {}
-        public void receiveBegin() {}
-        public void receiveBlog(String name, String blogKeyHash, String blogPath, long blogEntryId, List blogArchiveLocations, String anchorText) {}
-        public void receiveBold(String text) {}
-        public void receiveCode(String text, String codeLocationSchema, String codeLocation) {}
-        public void receiveCut(String summaryText) {}
-        public void receiveEnd() {}
-        public void receiveGT() {}
-        public void receiveH1(String text) {}
-        public void receiveH2(String text) {}
-        public void receiveH3(String text) {}
-        public void receiveH4(String text) {}
-        public void receiveH5(String text) {}
-        public void receiveHR() {}
-        public void receiveHeaderEnd() {}
-        public void receiveImage(String alternateText, int attachmentId) {}
-        public void receiveItalic(String text) {}
-        public void receiveLT() {}
-        public void receiveLeftBracket() {}
-        public void receiveLink(String schema, String location, String text) {}
-        public void receiveNewline() {}
-        public void receivePlain(String text) {}
-        public void receivePre(String text) {}
-        public void receiveQuote(String text, String whoQuoted, String quoteLocationSchema, String quoteLocation) {}
-        public void receiveRightBracket() {}
-        public void receiveUnderline(String text) {}
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/BlogManager.java b/apps/syndie/java/src/net/i2p/syndie/BlogManager.java
deleted file mode 100644
index 2eefba784014ab5f0bb17bfbbf01fc0977069640..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/BlogManager.java
+++ /dev/null
@@ -1,1142 +0,0 @@
-package net.i2p.syndie;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Properties;
-import java.util.StringTokenizer;
-import java.util.TimeZone;
-
-import net.i2p.I2PAppContext;
-import net.i2p.client.naming.PetName;
-import net.i2p.client.naming.PetNameDB;
-import net.i2p.data.Base64;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Destination;
-import net.i2p.data.Hash;
-import net.i2p.data.SessionKey;
-import net.i2p.data.SigningPrivateKey;
-import net.i2p.data.SigningPublicKey;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.syndie.sml.HTMLRenderer;
-import net.i2p.util.Log;
-
-/**
- *
- */
-public class BlogManager {
-    private I2PAppContext _context;
-    private Log _log;
-    private static BlogManager _instance;
-    private File _blogKeyDir;
-    private File _privKeyDir;
-    private File _archiveDir;
-    private File _userDir;
-    private File _cacheDir;
-    private File _tempDir;
-    private File _rootDir;
-    private Archive _archive;
-    
-    static {
-        TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
-    }
-    
-    public static BlogManager instance() { 
-        synchronized (BlogManager.class) {
-            if (_instance == null) {
-                String rootDir = I2PAppContext.getGlobalContext().getProperty("syndie.rootDir");
-                if (false) {
-                    if (rootDir == null)
-                        rootDir = System.getProperty("user.home");
-                    rootDir = rootDir + File.separatorChar + ".syndie";
-                } else {
-                    if (rootDir == null)
-                        rootDir = "./syndie";
-                }
-                _instance = new BlogManager(I2PAppContext.getGlobalContext(), rootDir, false);
-                _instance.getArchive().regenerateIndex();
-            }
-            return _instance; 
-        }
-    }
-    
-    public BlogManager(I2PAppContext ctx, String rootDir) { this(ctx, rootDir, true); }
-    public BlogManager(I2PAppContext ctx, String rootDir, boolean regenIndex) {
-        _context = ctx;
-        _log = ctx.logManager().getLog(BlogManager.class);
-        _rootDir = new File(rootDir);
-        _rootDir.mkdirs();
-        readConfig();
-        _blogKeyDir = new File(_rootDir, "blogkeys");
-        _privKeyDir = new File(_rootDir, "privkeys");
-        String archiveDir = _context.getProperty("syndie.archiveDir");
-        if (archiveDir != null)
-            _archiveDir = new File(archiveDir);
-        else
-            _archiveDir = new File(_rootDir, "archive");
-        _userDir = new File(_rootDir, "users");
-        _cacheDir = new File(_rootDir, "cache");
-        _tempDir = new File(_rootDir, "temp");
-        _blogKeyDir.mkdirs();
-        _privKeyDir.mkdirs();
-        _archiveDir.mkdirs();
-        _cacheDir.mkdirs();
-        _userDir.mkdirs();
-        _tempDir.mkdirs();
-        _archive = new Archive(ctx, _archiveDir.getAbsolutePath(), _cacheDir.getAbsolutePath(), this);
-        if (regenIndex)
-            _archive.regenerateIndex();
-    }
-    
-    private File getConfigFile() { return new File(_rootDir, "syndie.config"); }
-    private void readConfig() {
-        File config = getConfigFile();
-        if (config.exists()) {
-            try {
-                Properties p = new Properties();
-                DataHelper.loadProps(p, config);
-                for (Iterator iter = p.keySet().iterator(); iter.hasNext(); ) {
-                    String key = (String)iter.next();
-                    System.setProperty(key, p.getProperty(key));
-                    if (_log.shouldLog(Log.DEBUG))
-                        _log.debug("Read config prop [" + key + "] = [" + p.getProperty(key) + "]");
-                }
-            } catch (IOException ioe) {
-                if (_log.shouldLog(Log.DEBUG))
-                        _log.debug("Err reading", ioe);
-            }
-        } else {
-            if (_log.shouldLog(Log.DEBUG))
-                _log.debug("Config doesn't exist: " + config.getPath());
-        }
-    }
-    
-    public void writeConfig() {
-        File config = new File(_rootDir, "syndie.config");
-        FileOutputStream out = null;
-        try {
-            out = new FileOutputStream(config);
-            for (Iterator iter = _context.getPropertyNames().iterator(); iter.hasNext(); ) {
-                String name = (String)iter.next();
-                if (name.startsWith("syndie."))
-                    out.write(DataHelper.getUTF8(name + '=' + _context.getProperty(name) + '\n'));
-            }
-        } catch (IOException ioe) {
-            _log.error("Error writing the config", ioe);
-        } finally {
-            if (out != null) try { out.close(); } catch (IOException ioe) {}
-        }
-    }
-    
-    public BlogInfo createBlog(String name, String description, String contactURL, String archives[]) {
-        return createBlog(name, null, description, contactURL, archives);
-    }
-    public BlogInfo createBlog(String name, SigningPublicKey posters[], String description, String contactURL, String archives[]) {
-        Object keys[] = _context.keyGenerator().generateSigningKeypair();
-        SigningPublicKey pub = (SigningPublicKey)keys[0];
-        SigningPrivateKey priv = (SigningPrivateKey)keys[1];
-        
-        try {
-            FileOutputStream out = new FileOutputStream(new File(_privKeyDir, Base64.encode(pub.calculateHash().getData()) + ".priv"));
-            pub.writeBytes(out);
-            priv.writeBytes(out);
-        } catch (DataFormatException dfe) {
-            _log.error("Error creating the blog", dfe);
-            return null;
-        } catch (IOException ioe) {
-            _log.error("Error creating the blog", ioe);
-            return null;
-        }
-        
-        return createInfo(pub, priv, name, posters, description, contactURL, archives, 0);
-    }
-    
-    public BlogInfo createInfo(SigningPublicKey pub, SigningPrivateKey priv, String name, SigningPublicKey posters[], 
-                               String description, String contactURL, String archives[], int edition) {
-        Properties opts = new Properties();
-        if (name == null) name = "";
-        opts.setProperty("Name", name);
-        if (description == null) description = "";
-        opts.setProperty("Description", description);
-        opts.setProperty("Edition", Integer.toString(edition));
-        if (contactURL == null) contactURL = "";
-        opts.setProperty("ContactURL", contactURL);
-        for (int i = 0; archives != null && i < archives.length; i++) 
-            opts.setProperty("Archive." + i, archives[i]);
-        
-        BlogInfo info = new BlogInfo(pub, posters, opts);
-        info.sign(_context, priv);
-        
-        _archive.storeBlogInfo(info);
-        
-        return info;
-    }
-    
-    public boolean updateMetadata(User user, Hash blog, Properties opts) {
-        if (!user.getAuthenticated()) return false;
-        BlogInfo oldInfo = getArchive().getBlogInfo(blog);
-        if (oldInfo == null) return false;
-        if (!user.getBlog().equals(oldInfo.getKey().calculateHash())) return false;
-        int oldEdition = 0;
-        try { 
-            String ed = oldInfo.getProperty("Edition");
-            if (ed != null)
-                oldEdition = Integer.parseInt(ed);
-        } catch (NumberFormatException nfe) {}
-        opts.setProperty("Edition", oldEdition + 1 + "");
-        BlogInfo info = new BlogInfo(oldInfo.getKey(), oldInfo.getPosters(), opts);
-        SigningPrivateKey key = getMyPrivateKey(oldInfo);
-        info.sign(_context, key);
-        getArchive().storeBlogInfo(info);
-        user.setLastMetaEntry(oldEdition+1);
-        saveUser(user);
-        return true;
-    }
-    
-    public Archive getArchive() { return _archive; }
-    public File getTempDir() { return _tempDir; }
-    public File getRootDir() { return _rootDir; }
-    
-    public List listMyBlogs() {
-        File files[] = _privKeyDir.listFiles();
-        List rv = new ArrayList();
-        for (int i = 0; i < files.length; i++) {
-            if (files[i].isFile() && !files[i].isHidden()) {
-                FileInputStream in = null;
-                try {
-                    SigningPublicKey pub = new SigningPublicKey();
-                    in = new FileInputStream(files[i]);
-                    pub.readBytes(in);
-                    BlogInfo info = _archive.getBlogInfo(pub.calculateHash());
-                    if (info != null)
-                        rv.add(info);
-                } catch (IOException ioe) {
-                    _log.error("Error listing the blog", ioe);
-                } catch (DataFormatException dfe) {
-                    _log.error("Error listing the blog", dfe);
-                } finally {
-                    if (in != null) try { in.close(); } catch (IOException ioe) {}
-                }
-            }
-        }
-        return rv;
-    }
-    
-    public SigningPrivateKey getMyPrivateKey(BlogInfo blog) {
-        if (blog == null) return null;
-        File keyFile = new File(_privKeyDir, Base64.encode(blog.getKey().calculateHash().getData()) + ".priv");
-        FileInputStream in = null;
-        try {
-            in = new FileInputStream(keyFile);
-            SigningPublicKey pub = new SigningPublicKey();
-            pub.readBytes(in);
-            SigningPrivateKey priv = new SigningPrivateKey();
-            priv.readBytes(in);
-            return priv;
-        } catch (IOException ioe) {
-            _log.error("Error reading the blog key", ioe);
-            return null;
-        } catch (DataFormatException dfe) {
-            _log.error("Error reading the blog key", dfe);
-            return null;
-        } finally {
-            if (in != null) try { in.close(); } catch (IOException ioe) {}
-        }
-    }
-    
-    public User getUser(Hash blog) {
-        File files[] = _userDir.listFiles();
-        for (int i = 0; i < files.length; i++) {
-            if (files[i].isFile() && !files[i].isHidden()) {
-                Properties userProps = loadUserProps(files[i]);
-                if (userProps == null)
-                    continue;
-                User user = new User(_context);
-                user.load(userProps);
-                if (blog.equals(user.getBlog()))
-                    return user;
-            }
-        }
-        return null;
-    }
-    
-    /**
-     * List of User instances
-     */
-    public List listUsers() {
-        File files[] = _userDir.listFiles();
-        List rv = new ArrayList();
-        for (int i = 0; i < files.length; i++) {
-            if (files[i].isFile() && !files[i].isHidden()) {
-                Properties userProps = loadUserProps(files[i]);
-                if (userProps == null)
-                    continue;
-                User user = new User(_context);
-                user.load(userProps);
-                rv.add(user);
-            }
-        }
-        return rv;
-    }
-    
-    private Properties loadUserProps(File userFile) {
-        BufferedReader in = null;
-        try {
-            Properties props = new Properties();
-            FileInputStream fin = new FileInputStream(userFile);
-            in = new BufferedReader(new InputStreamReader(fin, "UTF-8"));
-            String line = null;
-            while ( (line = in.readLine()) != null) {
-                int split = line.indexOf('=');
-                if (split <= 0) continue;
-                String key = line.substring(0, split);
-                String val = line.substring(split+1);
-                props.setProperty(key.trim(), val.trim());
-            }
-            String userHash = userFile.getName();
-            props.setProperty(User.PROP_USERHASH, userHash);
-            return props;
-        } catch (IOException ioe) {
-            return null;
-        } finally { 
-            if (in != null) try { in.close(); } catch (IOException ioe) {}
-        }
-    }
-    
-    public boolean changePasswrd(User user, String oldPass, String pass0, String pass1) {
-        boolean ok = user.changePassword(oldPass, pass0, pass1);
-        if (ok)
-            saveUser(user);
-        return ok;
-    }
-
-    
-    public User login(String login, String pass) {
-        User u = new User(_context);
-        String ok = login(u, login, pass);
-        if (User.LOGIN_OK.equals(ok))
-            return u;
-        else
-            return new User(_context);
-    }
-    
-    public String login(User user, String login, String pass) {
-        if ( (login == null) || (pass == null) ) return "<span class=\"b_loginMsgErr\">Login not specified</span>";
-        Hash userHash = _context.sha().calculateHash(DataHelper.getUTF8(login));
-        Hash passHash = _context.sha().calculateHash(DataHelper.getUTF8(pass));
-        File userFile = new File(_userDir, Base64.encode(userHash.getData()));
-        if (_log.shouldLog(Log.INFO))
-            _log.info("Attempting to login to " + login + " w/ pass = " + pass 
-                           + ": file = " + userFile.getAbsolutePath() + " passHash = "
-                           + Base64.encode(passHash.getData()));
-        if (userFile.exists()) {
-            try {
-                Properties props = loadUserProps(userFile);
-                if (props == null) throw new IOException("Error reading " + userFile);
-                String rv = user.login(login, pass, props);
-                if (User.LOGIN_OK.equals(rv))
-                    _log.info("Login successful");
-                else
-                    _log.info("Login failed: [" + rv + "]");
-                return rv;
-            } catch (IOException ioe) {
-                _log.error("Error logging in", ioe);
-                return "<span class=\"b_loginMsgErr\">Error logging in - corrupt userfile</span>";
-            }
-        } else {
-            if (_log.shouldLog(Log.INFO))
-                _log.info("User does not exist");
-            return "<span class=\"b_loginMsgErr\">User does not exist</span>";
-        }
-    }
-    
-    /** hash of the password required to register and create a new blog (null means no password required) */
-    public String getRegistrationPasswordHash() { 
-        String pass = _context.getProperty("syndie.registrationPassword");
-        if ( (pass == null) || (pass.trim().length() <= 0) ) return null;
-        return pass; 
-    }
-    
-    /** Password required to access the remote syndication functinoality (null means no password required) */
-    public String getRemotePasswordHash() { 
-        String pass = _context.getProperty("syndie.remotePassword");
-        
-        if ( (pass == null) || (pass.trim().length() <= 0) ) return null;
-        return pass;
-    }
-    public String getAdminPasswordHash() { 
-        String pass = _context.getProperty("syndie.adminPassword");
-        if ( (pass == null) || (pass.trim().length() <= 0) ) return "";
-        return pass;
-    }
-    
-    public boolean isConfigured() {
-        String p = _context.getProperty("syndie.singleUser");
-        if(p==null)
-            return false;
-        return true;
-    }
-    
-    private static final boolean DEFAULT_IS_SINGLEUSER = true;
-    
-    /**
-     * If true, this syndie instance is meant for just one local user, so we don't need
-     * to password protect registration, remote.jsp, or admin.jsp
-     *
-     */
-    public boolean isSingleUser() {
-        if (!isConfigured()) return DEFAULT_IS_SINGLEUSER;
-        String isSingle = _context.getProperty("syndie.singleUser");
-        return ( (isSingle != null) && (Boolean.valueOf(isSingle).booleanValue()) );
-    }
-
-    public String getDefaultProxyHost() { return _context.getProperty("syndie.defaultProxyHost", "localhost"); }
-    public String getDefaultProxyPort() { return _context.getProperty("syndie.defaultProxyPort", "4444"); }
-    public String[] getUpdateArchives() { 
-        String str = _context.getProperty("syndie.updateArchives", "");
-        if ( (str != null) && (str.trim().length() > 0) )
-            return str.split(",");
-        else
-            return new String[0];
-    }
-    public boolean getImportAddresses() { return _context.getProperty("syndie.importAddresses", "false").equals("true"); }
-    public int getUpdateDelay() { 
-        int delay = Integer.parseInt(_context.getProperty("syndie.updateDelay", "12"));
-        if (delay < 1) delay = 1;
-        return delay;
-    }
-    
-    public List getRssFeeds() {
-        List feedList = new ArrayList();
-        int i=0;
-        while(true) {
-            String url = _context.getProperty("syndie.rssFeed."+i+".url");
-            String blog = _context.getProperty("syndie.rssFeed."+i+".blog");
-            String tagPrefix = _context.getProperty("syndie.rssFeed."+i+".tagPrefix");
-            if(url==null || blog==null || tagPrefix==null)
-                break;
-            String feed[] = new String[3];
-            feed[0]=url.trim();
-            feed[1]=blog.trim();
-            feed[2]=tagPrefix.trim();
-            feedList.add(feed);
-            i++;
-        }
-        return feedList;
-    }
-    public boolean addRssFeed(String url, String blog, String tagPrefix) {
-        
-        List feedList = getRssFeeds();
-        int nextIdx=feedList.size();
-        
-        String baseFeedProp="syndie.rssFeed."+nextIdx;
-        System.setProperty(baseFeedProp+".url",url);
-        System.setProperty(baseFeedProp+".blog",blog);
-        System.setProperty(baseFeedProp+".tagPrefix",tagPrefix);
-        _log.info("addRssFeed("+nextIdx+"): "+url);
-        writeConfig();
-        Updater.wakeup();
-        return true;
-    }
-    public boolean deleteRssFeed(String url, String blog, String tagPrefix) {
-        List feedList = getRssFeeds();
-        Iterator iter = feedList.iterator();
-        int idx=0;
-        while(iter.hasNext()) {
-            String fields[] = (String[])iter.next();
-            if(fields[0].equals(url) &&
-               fields[1].equals(blog) &&
-               fields[2].equals(tagPrefix)) {
-                break;
-            }
-            idx++;
-        }
-        
-        // copy any remaining to idx-1
-        while(iter.hasNext()) {
-            String fields[] = (String[])iter.next();
-            String baseFeedProp="syndie.rssFeed."+idx;
-            System.setProperty(baseFeedProp+".url",fields[0]);
-            System.setProperty(baseFeedProp+".blog",fields[1]);
-            System.setProperty(baseFeedProp+".tagPrefix",fields[2]);
-            idx++;
-        }
-        
-        // Delete last idx from properties
-        String baseFeedProp="syndie.rssFeed."+idx;
-        System.getProperties().remove(baseFeedProp+".url");
-        System.getProperties().remove(baseFeedProp+".blog");
-        System.getProperties().remove(baseFeedProp+".tagPrefix");
-        _log.info("deleteRssFeed("+idx+"): "+url);
-        writeConfig();
-        return true;
-    }
-     
-    private static final String DEFAULT_LOGIN = "default";
-    private static final String DEFAULT_PASS = "";
-    
-    private static final String PROP_DEFAULT_LOGIN = "syndie.defaultSingleUserLogin";
-    private static final String PROP_DEFAULT_PASS = "syndie.defaultSingleUserPass";
-    
-    public String getDefaultLogin() {
-        String login = _context.getProperty(PROP_DEFAULT_LOGIN);
-        if ( (login == null) || (login.trim().length() <= 0) )
-            login = DEFAULT_LOGIN;
-        return login;
-    }
-    public String getDefaultPass() {
-        String pass = _context.getProperty(PROP_DEFAULT_PASS);
-        if ( (pass == null) || (pass.trim().length() <= 0) )
-            pass = DEFAULT_PASS;
-        return pass;
-    }
-
-    /**
-     * If we are a single user instance, when we create the default user, give them
-     * addressbook entries for each of the following, *and* schedule them for syndication
-     *
-     */
-    private static final String DEFAULT_SINGLE_USER_ARCHIVES[] = new String[] {
-        "http://syndiemedia.i2p/archive/archive.txt"
-        , "http://gloinsblog.i2p/archive/archive.txt"
-        , "http://glog.i2p/archive/archive.txt"
-    };
-    
-    public User getDefaultUser() {
-        User user = new User(_context);
-        getDefaultUser(user);
-        return user;
-    }
-    public void getDefaultUser(User user) {
-        if (isSingleUser()) {
-            Hash userHash = _context.sha().calculateHash(DataHelper.getUTF8(getDefaultLogin()));
-            File userFile = new File(_userDir, Base64.encode(userHash.getData()));
-            if (_log.shouldLog(Log.INFO))
-                _log.info("Attempting to login to the default user: " + userFile.getAbsolutePath());
-            
-            if (userFile.exists()) {
-                Properties props = loadUserProps(userFile);
-                if (props == null) {
-                    user.invalidate();
-                    _log.error("Error reading the default user file: " + userFile);
-                    return;
-                }
-                String ok = user.login(getDefaultLogin(), getDefaultPass(), props);
-                if (User.LOGIN_OK.equals(ok)) {
-                    return;
-                } else {
-                    user.invalidate();
-                    _log.error("Error logging into the default user: " + ok);
-                    return;
-                }
-            } else {
-                String ok = register(user, getDefaultLogin(), getDefaultPass(), "", "default", "Default Syndie blog", "");
-                if (User.LOGIN_OK.equals(ok)) {
-                    _log.info("Default user created: " + user);
-                    String altArchives = _context.getProperty("syndie.defaultSingleUserArchives");
-                    String archives[] = DEFAULT_SINGLE_USER_ARCHIVES;
-                    if ( (altArchives != null) && (altArchives.trim().length() > 0) ) {
-                        ArrayList list = new ArrayList();
-                        StringTokenizer tok = new StringTokenizer(altArchives, ",\t ");
-                        while (tok.hasMoreTokens())
-                            list.add(tok.nextToken());
-                        if (list.size() > 0) {
-                            archives = new String[list.size()];
-                            for (int i = 0; i < list.size(); i++)
-                                archives[i] = (String)list.get(i);
-                        }
-                    }
-                    for (int i = 0; i < archives.length; i++)
-                        user.getPetNameDB().add(new PetName("DefaultArchive" + i, "syndie", "syndiearchive", archives[i]));
-                    scheduleSyndication(archives);
-                    saveUser(user);
-                    return;
-                } else {
-                    user.invalidate();
-                    _log.error("Error registering the default user: " + ok);
-                    return;
-                }
-            }
-        } else {
-            return;
-        }
-    }
-    
-    public boolean authorizeAdmin(String pass) {
-        if (isSingleUser()) return true;
-        String admin = getAdminPasswordHash();
-        if ( (admin == null) || (admin.trim().length() <= 0) )
-            return false;
-        String hash = Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(pass.trim())).getData());
-        return (hash.equals(admin));
-    }
-    public boolean authorizeRemote(String pass) {
-        if (isSingleUser()) return true;
-        String hash = Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(pass.trim())).getData());
-        String rem = getRemotePasswordHash();
-        boolean ok = false;
-        if ( (rem != null) && (rem.trim().length() > 0) )
-            ok = hash.equals(rem);
-        if (!ok) {
-            rem = getAdminPasswordHash();
-            if ( (rem != null) && (rem.trim().length() > 0) )
-                ok = hash.equals(rem);
-        }
-        return ok;
-    }
-    public boolean authorizeRemote(User user) {
-        if (isSingleUser()) return true;
-        return (user.getAuthenticated() && user.getAllowAccessRemote());
-    }
-    
-    private boolean isOkDefaultUser(String defaultUser, String defaultPass) {
-        User t = new User(_context);
-        Hash userHash = _context.sha().calculateHash(DataHelper.getUTF8(defaultUser));
-        File userFile = new File(_userDir, Base64.encode(userHash.getData()));
-        if (userFile.exists()) {
-            Properties props = loadUserProps(userFile);
-            if (props == null) {
-                _log.error("Error reading the default user file: " + userFile);
-                return false;
-            }
-            String ok = t.login(defaultUser, defaultPass, props);
-            if (User.LOGIN_OK.equals(ok)) {
-                // ok, good enough
-                return true;
-            } else {
-                _log.error("Error logging into the default user, so configuration change rejected: " + ok);
-                return false;
-            }
-        } else {
-            _log.error("Not setting the default user to a nonexistant user [" + defaultUser + "]");
-            return false;
-        }
-    }
-    
-    public void configure(String registrationPassword, String remotePassword, String adminPass, String defaultSelector, 
-                          String defaultProxyHost, int defaultProxyPort, boolean isSingleUser, Properties opts,
-                          String defaultUser, String defaultPass) {
-        if ( (defaultUser == null) || (defaultUser.length() <= 0) )
-            defaultUser = getDefaultLogin();
-        if (defaultPass == null)
-            defaultPass = getDefaultPass();
-        // first make sure the default user is valid, if its single user
-        if (isSingleUser) {
-            if (!isOkDefaultUser(defaultUser, defaultPass))
-                return;
-        }
-        File cfg = getConfigFile();
-        Writer out = null;
-        try {
-            out = new OutputStreamWriter(new FileOutputStream(cfg), "UTF-8");
-            if (registrationPassword != null)
-                out.write("syndie.registrationPassword="+Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(registrationPassword.trim())).getData()) + "\n");
-            if (remotePassword != null)
-                out.write("syndie.remotePassword="+Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(remotePassword.trim())).getData()) + "\n");
-            if (adminPass != null)
-                out.write("syndie.adminPassword="+Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(adminPass.trim())).getData()) + "\n");
-            if (defaultSelector != null)
-                out.write("syndie.defaultSelector="+defaultSelector.trim() + "\n");
-            if (defaultProxyHost != null)
-                out.write("syndie.defaultProxyHost="+defaultProxyHost.trim() + "\n");
-            if (defaultProxyPort > 0)
-                out.write("syndie.defaultProxyPort="+defaultProxyPort + "\n");
-            
-            out.write("syndie.defaultSingleUserLogin="+defaultUser+"\n");
-            out.write("syndie.defaultSingleUserPass="+defaultPass+"\n");
-            
-            out.write("syndie.singleUser=" + isSingleUser + "\n"); // Used also in isConfigured()
-            if (opts != null) {
-                for (Iterator iter = opts.keySet().iterator(); iter.hasNext(); ) {
-                    String key = (String)iter.next();
-                    String val = opts.getProperty(key);
-                    out.write(key.trim() + "=" + val.trim() + "\n");
-                }
-            }
-            _archive.setDefaultSelector(defaultSelector);
-        } catch (IOException ioe) {
-            _log.error("Error writing out the config", ioe);
-        } finally {
-            if (out != null) try { out.close(); } catch (IOException ioe) {}
-            readConfig();
-        }
-    }
-    
-    public String authorizeRemoteAccess(User user, String password) {
-        if (!user.getAuthenticated()) return "<span class=\"b_remoteMsgErr\">Not logged in</span>";
-        String remPass = getRemotePasswordHash();
-        if (remPass == null)
-            return "<span class=\"b_remoteMsgErr\">Remote access password not configured - please <a href=\"admin.jsp\">specify</a> a remote " +
-                   "archive password</span>";
-        
-        if (authorizeRemote(password)) {
-            user.setAllowAccessRemote(true);
-            saveUser(user);
-            return "<span class=\"b_remoteMsgOk\">Remote access authorized</span>";
-        } else {
-            return "<span class=\"b_remoteMsgErr\">Remote access denied</span>";
-        }
-    }
-    
-    /**
-     * Store user info, regardless of whether they're logged in.  This lets you update a
-     * different user's info!
-     */
-    void storeUser(User user) {
-        String userHash = user.getUserHash();
-        File userFile = new File(_userDir, userHash);
-        if (!userFile.exists()) return;
-        FileOutputStream out = null;
-        try {
-            out = new FileOutputStream(userFile);
-            out.write(DataHelper.getUTF8(user.export()));
-            user.getPetNameDB().store(user.getAddressbookLocation());
-        } catch (IOException ioe) {
-            _log.error("Error writing out the user", ioe);
-        } finally {
-            if (out != null) try { out.close(); } catch (IOException ioe){}
-        }
-    }
-    
-    public void saveUser(User user) {
-        if (!user.getAuthenticated()) return;
-        storeUser(user);
-    }
-    public User register(String login, String password, String registrationPassword, String blogName, String blogDescription, String contactURL) {
-        User user = new User(_context);
-        if (User.LOGIN_OK.equals(register(user, login, password, registrationPassword, blogName, blogDescription, contactURL)))
-            return user;
-        else
-            return null;
-    }
-    public String register(User user, String login, String password, String registrationPassword, String blogName, String blogDescription, String contactURL) {
-        System.err.println("Register [" + login + "] pass [" + password + "] name [" + blogName + "] descr [" + blogDescription + "] contact [" + contactURL + "] regPass [" + registrationPassword + "]");
-        String hashedRegistrationPassword = getRegistrationPasswordHash();
-        if ( (hashedRegistrationPassword != null) && (!isSingleUser()) ) {
-            try {
-                if (!hashedRegistrationPassword.equals(Base64.encode(_context.sha().calculateHash(registrationPassword.getBytes("UTF-8")).getData())))
-                    return "<span class=\"b_regMsgErr\">Invalid registration password</span>";
-            } catch (UnsupportedEncodingException uee) {
-                return "<span class=\"b_regMsgErr\">Error registering</span>";
-            }
-        }
-        String userHash = Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(login)).getData());
-        File userFile = new File(_userDir, userHash);
-        if (userFile.exists()) {
-            return "<span class=\"b_regMsgErr\">Cannot register the login " + login + ": it already exists</span>";
-        } else {
-            BlogInfo info = createBlog(blogName, blogDescription, contactURL, null);
-            String hashedPassword = Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(password)).getData());
-            FileOutputStream out = null;
-            try {
-                out = new FileOutputStream(userFile);
-                BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
-                bw.write("password=" + hashedPassword + "\n");
-                bw.write("blog=" + Base64.encode(info.getKey().calculateHash().getData()) + "\n");
-                bw.write("lastid=-1\n");
-                bw.write("lastmetaedition=0\n");
-                bw.write("addressbook=userhosts-"+userHash + ".txt\n");
-                bw.write("showimages=false\n");
-                bw.write("showexpanded=false\n");
-                bw.flush();
-            } catch (IOException ioe) {
-                _log.error("Error registering the user", ioe);
-                return "<span class=\"b_regMsgErr\">Internal error registering - " + ioe.getMessage() + "</span>";
-            } finally {
-                if (out != null) try { out.close(); } catch (IOException ioe) {}
-            }
-            String loginResult = login(user, login, password);
-            _archive.regenerateIndex();
-            return loginResult;
-        }
-    }
-
-    public String exportHosts(User user) {
-        if (!user.getAuthenticated() || !user.getAllowAccessRemote())
-            return "<span class=\"b_addrMsgErr\">Not authorized to export the hosts</span>";
-        PetNameDB userDb = user.getPetNameDB();
-        PetNameDB routerDb = _context.petnameDb();
-        // horribly inefficient...
-        for (Iterator iter = userDb.iterator(); iter.hasNext();) {
-            PetName pn = (PetName)iter.next();
-            if (pn == null) continue;
-            Destination existing = _context.namingService().lookup(pn.getName());
-            if (existing == null && pn.getNetwork().equalsIgnoreCase("i2p")) {
-                routerDb.add(pn);
-                try {
-                    routerDb.store();
-                } catch (IOException ioe) {
-                    _log.error("Error exporting the hosts", ioe);
-                    return "<span class=\"b_addrMsgErr\">Error exporting the hosts: " + ioe.getMessage() + "</span>";
-                }
-            }
-        }
-        return "<span class=\"b_addrMsgOk\">Hosts exported</span>";
-    }
-    
-    /**
-     * Guess what the next entry ID should be for the given user.  Rounds down to 
-     * midnight of the current day + 1 for each post in that day.
-     */
-    public long getNextBlogEntry(User user) {
-        long entryId = -1;
-        long now = _context.clock().now();
-        long dayBegin = getDayBegin(now);
-        if (user.getMostRecentEntry() >= dayBegin)
-            entryId = user.getMostRecentEntry() + 1;
-        else
-            entryId = dayBegin;
-        return entryId;
-    }
-    
-    public BlogURI createBlogEntry(User user, String subject, String tags, String entryHeaders, String sml) {
-        return createBlogEntry(user, true, subject, tags, entryHeaders, sml, null, null, null);
-    }
-    public BlogURI createBlogEntry(User user, String subject, String tags, String entryHeaders, String sml, List fileNames, List fileStreams, List fileTypes) {
-        return createBlogEntry(user, true, subject, tags, entryHeaders, sml, fileNames, fileStreams, fileTypes);        
-    }
-    public BlogURI createBlogEntry(User user, boolean shouldAuthenticate, String subject, String tags, String entryHeaders, String sml, List fileNames, List fileStreams, List fileTypes) {
-        if (shouldAuthenticate && !user.getAuthenticated()) return null;
-        BlogInfo info = getArchive().getBlogInfo(user.getBlog());
-        if (info == null) return null;
-        SigningPrivateKey privkey = getMyPrivateKey(info);
-        if (privkey == null) return null;
-        
-        long entryId = getNextBlogEntry(user);
-        
-        _log.debug("Next blog entry ID = " + entryId + " for user " + user.getUsername());
-        
-        StringTokenizer tok = new StringTokenizer(tags, " ,\n\t");
-        String tagList[] = new String[tok.countTokens()];
-        for (int i = 0; i < tagList.length; i++) 
-            tagList[i] = tok.nextToken().trim();
-        
-        BlogURI uri = new BlogURI(user.getBlog(), entryId);
-        
-        try {
-            StringBuffer raw = new StringBuffer(sml.length() + 128);
-            raw.append("Subject: ").append(subject).append('\n');
-            raw.append("Tags: ");
-            for (int i = 0; i < tagList.length; i++) 
-                raw.append(tagList[i]).append('\t');
-            raw.append('\n');
-            if ( (entryHeaders != null) && (entryHeaders.trim().length() > 0) ) {
-                if (_log.shouldLog(Log.DEBUG))
-                    _log.debug("Creating entry with headers: " + entryHeaders);
-                BufferedReader userHeaders = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(DataHelper.getUTF8(entryHeaders)), "UTF-8"));
-                String line = null;
-                while ( (line = userHeaders.readLine()) != null) {
-                    line = line.trim();
-                    if (_log.shouldLog(Log.DEBUG))
-                        _log.debug("header line: " + line);
-                    if (line.length() <= 0) continue;
-                    int split = line.indexOf('=');
-                    int split2 = line.indexOf(':');
-                    if ( (split < 0) || ( (split2 > 0) && (split2 < split) ) ) split = split2;
-                    if ( (split < 0) && (split2 < 0) )
-                        continue;
-                    String key = line.substring(0,split).trim();
-                    String val = line.substring(split+1).trim();
-                    raw.append(key).append(": ").append(val).append('\n');
-                }
-            }
-            raw.append('\n');
-            raw.append(sml);
-            
-            EntryContainer c = new EntryContainer(uri, tagList, DataHelper.getUTF8(raw));
-            if ((fileNames != null) && (fileStreams != null) && (fileNames.size() == fileStreams.size()) ) {
-                for (int i = 0; i < fileNames.size(); i++) {
-                    String name = (String)fileNames.get(i);
-                    InputStream in = (InputStream)fileStreams.get(i);
-                    String fileType = (fileTypes != null ? (String)fileTypes.get(i) : "application/octet-stream");
-                    ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
-                    byte buf[] = new byte[1024];
-                    while (true) {
-                        int read = in.read(buf);
-                        if (read == -1) break;
-                        baos.write(buf, 0, read);
-                    }
-                    byte att[] = baos.toByteArray();
-                    if ( (att != null) && (att.length > 0) )
-                        c.addAttachment(att, new File(name).getName(), null, fileType);
-                }
-            }
-            //for (int i = 7; i < args.length; i++) {
-            //    c.addAttachment(read(args[i]), new File(args[i]).getName(), 
-            //                    "Attached file", "application/octet-stream");
-            //}
-            SessionKey entryKey = null;
-            //if (!"NONE".equals(args[5]))
-            //    entryKey = new SessionKey(Base64.decode(args[5]));
-            c.seal(_context, privkey, null);
-            boolean ok = getArchive().storeEntry(c);
-            if (ok) {
-                getArchive().regenerateIndex();
-                long prevEntryId = user.getMostRecentEntry();
-                user.setMostRecentEntry(entryId);
-                if(shouldAuthenticate) {
-                    saveUser(user);
-                } else {
-                    storeUser(user);
-                }
-                _log.debug("New entry posted, entryId=" + entryId + " prev=" + prevEntryId);
-                return uri;
-            } else {
-                return null;
-            }
-        } catch (IOException ioe) {
-            _log.error("Error creating post", ioe);
-            return null;
-        }
-    }
-    
-    /** 
-     * read in the syndie blog metadata file from the stream, verifying it and adding it to 
-     * the archive if necessary
-     *
-     */
-    public boolean importBlogMetadata(InputStream metadataStream) throws IOException {
-        try {
-            BlogInfo info = new BlogInfo();
-            info.load(metadataStream);
-            if (isBanned(info.getKey().calculateHash()))
-                return false;
-            return _archive.storeBlogInfo(info);
-        } catch (IOException ioe) {
-            _log.error("Error importing meta", ioe);
-            return false;
-        }
-    }
-    
-    /** 
-     * read in the syndie entry file from the stream, verifying it and adding it to 
-     * the archive if necessary
-     *
-     */
-    public boolean importBlogEntry(InputStream entryStream) throws IOException {
-        try {
-            EntryContainer c = new EntryContainer();
-            c.load(entryStream);
-            if (isBanned(c.getURI().getKeyHash()))
-                return false;
-            return _archive.storeEntry(c);
-        } catch (IOException ioe) {
-            _log.error("Error importing entry", ioe);
-            return false;
-        }
-    }
-
-    public String addAddress(User user, String name, String protocol, String location, String schema) {
-        if (!user.getAuthenticated()) return "<span class=\"b_addrMsgErr\">Not logged in</span>";
-        boolean ok = validateAddressName(name);
-        if (!ok) return "<span class=\"b_addrMsgErr\">Invalid name: " + HTMLRenderer.sanitizeString(name) + "</span>";
-        ok = validateAddressLocation(location);
-        if (!ok) return "<span class=\"b_addrMsgErr\">Invalid location: " + HTMLRenderer.sanitizeString(location) + "</span>";
-        if (!validateAddressSchema(schema)) return "<span class=\"b_addrMsgErr\">Unsupported schema: " + HTMLRenderer.sanitizeString(schema) + "</span>";
-        // no need to quote user/location further, as they've been sanitized
-        
-        PetNameDB names = user.getPetNameDB();
-        if (names.containsName(name))
-            return "<span class=\"b_addrMsgErr\">Name is already in use</span>";
-        PetName pn = new PetName(name, schema, protocol, location);
-        names.add(pn);
-        
-        try {
-            names.store(user.getAddressbookLocation());
-            return "<span class=\"b_addrMsgOk\">Address " + name + " written to your addressbook</span>";
-        } catch (IOException ioe) {
-            return "<span class=\"b_addrMsgErr\">Error writing out the name: " + ioe.getMessage() + "</span>";
-        }
-    }
-    
-    public Properties getKnownHosts(User user, boolean includePublic) throws IOException {
-        Properties rv = new Properties();
-        if ( (user != null) && (user.getAuthenticated()) ) {
-            File userHostsFile = new File(user.getAddressbookLocation());
-            rv.putAll(getKnownHosts(userHostsFile));
-        }
-        if (includePublic) {
-            rv.putAll(getKnownHosts(new File("hosts.txt")));
-        }
-        return rv;
-    }
-    private Properties getKnownHosts(File filename) throws IOException {
-        Properties rv = new Properties();
-        if (filename.exists()) {
-            FileInputStream in = null;
-            try {
-                in = new FileInputStream(filename);
-                rv.load(in);
-            } finally {
-                if (in != null) try { in.close(); } catch (IOException ioe) {}
-            }
-        }
-        return rv;
-    }
-    
-    private boolean validateAddressName(String name) {
-        if ( (name == null) || (name.trim().length() <= 0) ) return false;
-        for (int i = 0; i < name.length(); i++) {
-            char c = name.charAt(i);
-            if (!Character.isLetterOrDigit(c) && ('.' != c) && ('-' != c) && ('_' != c) )
-                return false;
-        }
-        return true;
-    }
-
-    private boolean validateAddressLocation(String location) {
-        if ( (location == null) || (location.trim().length() <= 0) ) return false;
-        if (false) {
-            try {
-                Destination d = new Destination(location);
-                return (d.getPublicKey() != null);
-            } catch (DataFormatException dfe) {
-                _log.error("Error validating address location", dfe);
-                return false;
-            }
-        } else {
-            // not everything is an i2p destination...
-            return true;
-        }
-    }
-
-    private boolean validateAddressSchema(String schema) {
-        if ( (schema == null) || (schema.trim().length() <= 0) ) return false;
-        if (true) {
-            return true;
-        } else {
-            return "eep".equals(schema) || "i2p".equals(schema);
-        }
-    }
-
-    private final SimpleDateFormat _dateFormat = new SimpleDateFormat("yyyy/MM/dd", Locale.UK);    
-    public final long getDayBegin() { return getDayBegin(_context.clock().now()); }
-    public final long getDayBegin(long now) {
-        synchronized (_dateFormat) {
-            try {
-                String str = _dateFormat.format(new Date(now));
-                return _dateFormat.parse(str).getTime();
-            } catch (ParseException pe) {
-                pe.printStackTrace();
-                // wtf
-                return -1;
-            }
-        }
-    }
-    
-    public void scheduleSyndication(String location) {
-        String archives[] = getUpdateArchives();
-        StringBuffer buf = new StringBuffer(64);
-        if ( (archives != null) && (archives.length > 0) ) {
-            for (int i = 0; i < archives.length; i++)
-                if ( (!archives[i].equals(location)) && (archives[i].trim().length() > 0) )
-                    buf.append(archives[i]).append(",");
-        }
-        if ( (location != null) && (location.trim().length() > 0) )
-            buf.append(location.trim());
-        System.setProperty("syndie.updateArchives", buf.toString());
-        writeConfig();
-        Updater.wakeup();
-    }
-    public void scheduleSyndication(String locations[]) {
-        String archives[] = getUpdateArchives();
-        HashSet locs = new HashSet();
-        for (int i = 0; (archives != null) && (i < archives.length); i++)
-            locs.add(archives[i]);
-        for (int i = 0; (locations != null) && (i < locations.length); i++)
-            locs.add(locations[i]);
-        
-        StringBuffer buf = new StringBuffer(64);
-        for (Iterator iter = locs.iterator(); iter.hasNext(); )
-            buf.append(iter.next().toString().trim()).append(',');
-        System.setProperty("syndie.updateArchives", buf.toString());
-        writeConfig();
-        Updater.wakeup();
-    }
-    public void unscheduleSyndication(String location) {
-        String archives[] = getUpdateArchives();
-        if ( (archives != null) && (archives.length > 0) ) {
-            StringBuffer buf = new StringBuffer(64);
-            for (int i = 0; i < archives.length; i++)
-                if ( (!archives[i].equals(location)) && (archives[i].trim().length() > 0) )
-                    buf.append(archives[i]).append(",");
-            System.setProperty("syndie.updateArchives", buf.toString());
-        }
-        writeConfig();
-    }
-    public boolean syndicationScheduled(String location) {
-        String archives[] = getUpdateArchives();
-        if ( (location == null) || (archives == null) || (archives.length <= 0) )
-            return false;
-        for (int i = 0; i < archives.length; i++)
-            if (location.equals(archives[i]))
-                return true;
-        return false;
-    }
-    
-    public boolean isBanned(Hash blog) {
-        if ( (blog == null) || (blog.getData() == null) || (blog.getData().length <= 0) ) return false;
-        String str = blog.toBase64();
-        String banned = System.getProperty("syndie.bannedBlogs", "");
-        return (banned.indexOf(str) >= 0);
-    }
-    
-    public String[] getBannedBlogs() {
-        List blogs = new ArrayList();
-        String str = System.getProperty("syndie.bannedBlogs", "");
-        StringTokenizer tok = new StringTokenizer(str, ",");
-        while (tok.hasMoreTokens()) {
-            String blog = tok.nextToken();
-            try {
-                Hash h = new Hash();
-                h.fromBase64(blog);
-                blogs.add(blog); // the base64 string, but verified
-            } catch (DataFormatException dfe) {
-                // ignored
-            }
-        }
-        String rv[] = new String[blogs.size()];
-        for (int i = 0; i < blogs.size(); i++)
-            rv[i] = (String)blogs.get(i);
-        return rv;
-    }
-    
-    /**
-     * Delete the blog from the archive completely, and ban them from ever being added again
-     */
-    public void purgeAndBan(Hash blog) {
-        String banned[] = getBannedBlogs();
-        StringBuffer buf = new StringBuffer();
-        String str = blog.toBase64();
-        buf.append(str);
-        for (int i = 0; banned != null && i < banned.length; i++) {
-            if (!banned[i].equals(str))
-                buf.append(",").append(banned[i]);
-        }
-        System.setProperty("syndie.bannedBlogs", buf.toString());
-        writeConfig();
-        _archive.delete(blog);
-        _archive.regenerateIndex();
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/CLI.java b/apps/syndie/java/src/net/i2p/syndie/CLI.java
deleted file mode 100644
index 556886d890c97b6e5929ade3b03cbd5c406a8dfe..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/CLI.java
+++ /dev/null
@@ -1,270 +0,0 @@
-package net.i2p.syndie;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.StringTokenizer;
-
-import net.i2p.I2PAppContext;
-import net.i2p.data.Base64;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Hash;
-import net.i2p.data.SessionKey;
-import net.i2p.data.SigningPrivateKey;
-import net.i2p.syndie.data.Attachment;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.syndie.sml.HTMLRenderer;
-
-/**
- */
-public class CLI {
-    public static final String USAGE = "Usage: \n" +
-        "rootDir regenerateIndex\n" +
-        "rootDir createBlog name description contactURL[ archiveURL]*\n" +
-        "rootDir createEntry blogPublicKeyHash tag[,tag]* (NEXT|NOW|entryId) (NONE|entryKeyBase64) smlFile[ attachmentFile attachmentName attachmentDescription mimeType]*\n" +
-        "rootDir listMyBlogs\n" +
-        "rootDir listTags blogPublicKeyHash\n" +
-        "rootDir listEntries blogPublicKeyHash blogTag\n" +
-        "rootDir renderEntry blogPublicKeyHash entryId (NONE|entryKeyBase64) summaryOnly includeImages\n";
-    
-    public static void main(String args[]) {
-        //args = new String[] { "~/.syndie/", "listEntries", "9qXCJUyUBCCaiIShURo02ckxjrMvrtiDYENv2ATL3-Y=", "/" };
-        //args = new String[] { "~/.syndie/", "renderEntry", "Vq~AlW-r7OM763okVUFIDvVFzxOjpNNsAx0rFb2yaE8=", "/", "20050811001", "NONE", "true", "false" };
-        if (args.length < 2) {
-            System.err.print(USAGE);
-            return;
-        }
-        String command = args[1];
-        if ("createBlog".equals(command))
-            createBlog(args);
-        else if ("listMyBlogs".equals(command))
-            listMyBlogs(args);
-        else if ("createEntry".equals(command))
-            createEntry(args);
-        else if ("listTags".equals(command))
-            listPaths(args);
-        else if ("listEntries".equals(command))
-            listEntries(args);
-        else if ("regenerateIndex".equals(command))
-            regenerateIndex(args);
-        else if ("renderEntry".equals(command))
-            renderEntry(args);
-        else
-            System.out.print(USAGE);
-    }
-    
-    private static void createBlog(String args[]) {
-        BlogManager mgr = new BlogManager(I2PAppContext.getGlobalContext(), args[0]);
-        String archives[] = new String[args.length - 5];
-        System.arraycopy(args, 5, archives, 0, archives.length);
-        BlogInfo info = mgr.createBlog(args[2], args[3], args[4], archives);
-        System.out.println("Blog created: " + info);
-        mgr.getArchive().regenerateIndex();
-    }
-    private static void listMyBlogs(String args[]) {
-        BlogManager mgr = new BlogManager(I2PAppContext.getGlobalContext(), args[0]);
-        List info = mgr.listMyBlogs();
-        for (int i = 0; i < info.size(); i++) 
-            System.out.println(info.get(i).toString());
-    }
-    
-    private static void listPaths(String args[]) {
-        // "rootDir listTags blogPublicKeyHash\n";
-        BlogManager mgr = new BlogManager(I2PAppContext.getGlobalContext(), args[0]);
-        List tags = mgr.getArchive().listTags(new Hash(Base64.decode(args[2])));
-        System.out.println("tag count: " + tags.size());
-        for (int i = 0; i < tags.size(); i++)
-            System.out.println("Tag " + i + ": " + tags.get(i).toString());
-    }
-
-    private static void regenerateIndex(String args[]) {
-        // "rootDir regenerateIndex\n";
-        BlogManager mgr = new BlogManager(I2PAppContext.getGlobalContext(), args[0]);
-        mgr.getArchive().regenerateIndex();
-        System.out.println("Index regenerated");
-    }
-
-    private static void listEntries(String args[]) {
-        // "rootDir listEntries blogPublicKeyHash tag\n";
-        BlogManager mgr = new BlogManager(I2PAppContext.getGlobalContext(), args[0]);
-        List entries = mgr.getArchive().listEntries(new Hash(Base64.decode(args[2])), -1, args[3], null);
-        System.out.println("Entry count: " + entries.size());
-        for (int i = 0; i < entries.size(); i++) {
-            EntryContainer entry = (EntryContainer)entries.get(i);
-            System.out.println("***************************************************");
-            System.out.println("Entry " + i + ": " + entry.getURI().toString());
-            System.out.println("===================================================");
-            System.out.println(entry.getEntry().getText());
-            System.out.println("===================================================");
-            Attachment attachments[] = entry.getAttachments();
-            for (int j = 0; j < attachments.length; j++) {
-                System.out.println("Attachment " + j + ": " + attachments[j]);
-            }
-            System.out.println("===================================================");
-        }
-    }
-    
-    private static void renderEntry(String args[]) {
-        //"rootDir renderEntry blogPublicKeyHash entryId (NONE|entryKeyBase64) summaryOnly includeImages\n";
-        BlogManager mgr = new BlogManager(I2PAppContext.getGlobalContext(), args[0]);
-        long id = -1;
-        try {
-            id = Long.parseLong(args[3]);
-        } catch (NumberFormatException nfe) {
-            nfe.printStackTrace();
-            return;
-        }
-        SessionKey entryKey = null;
-        if (!("NONE".equals(args[4]))) 
-            entryKey = new SessionKey(Base64.decode(args[5]));
-        EntryContainer entry = mgr.getArchive().getEntry(new BlogURI(new Hash(Base64.decode(args[2])), id), entryKey);
-        if (entry != null) {
-            HTMLRenderer renderer = new HTMLRenderer(I2PAppContext.getGlobalContext());
-            boolean summaryOnly = "true".equalsIgnoreCase(args[5]);
-            boolean showImages = "true".equalsIgnoreCase(args[6]);
-            try {
-                File f = File.createTempFile("syndie", ".html");
-                Writer out = new OutputStreamWriter(new FileOutputStream(f), "UTF-8");
-                renderer.render(null, mgr.getArchive(), entry, out, summaryOnly, showImages);
-                out.flush();
-                out.close();
-                System.out.println("Rendered to " + f.getAbsolutePath() + ": " + f.length());
-            } catch (IOException ioe) {
-                ioe.printStackTrace();
-            }
-        } else {
-            System.err.println("Entry does not exist");
-        }
-    }
-
-    private static void createEntry(String args[]) {
-        // "rootDir createEntry blogPublicKeyHash tag[,tag]* (NEXT|NOW|entryId) (NONE|entryKeyBase64) "
-        //  smlFile[ attachmentFile attachmentName attachmentDescription mimeType]*\n"
-        String rootDir = args[0];
-        String hashStr = args[2];
-        List tags = new ArrayList();
-        StringTokenizer tok = new StringTokenizer(args[3], ",");
-        while (tok.hasMoreTokens())
-            tags.add(tok.nextToken().trim());
-        String entryIdDef = args[4];
-        String entryKeyDef = args[5];
-        String smlFile = args[6];
-        List attachmentFilenames = new ArrayList();
-        List attachmentNames = new ArrayList();
-        List attachmentDescriptions = new ArrayList();
-        List attachmentMimeTypes = new ArrayList();
-        for (int i = 7; i + 3 < args.length; i += 4) {
-            attachmentFilenames.add(args[i].trim());
-            attachmentNames.add(args[i+1].trim());
-            attachmentDescriptions.add(args[i+2].trim());
-            attachmentMimeTypes.add(args[i+3].trim());
-        }
-        I2PAppContext ctx = I2PAppContext.getGlobalContext();
-        BlogManager mgr = new BlogManager(ctx, rootDir);
-        EntryContainer entry = createEntry(ctx, mgr, hashStr, tags, entryIdDef, entryKeyDef, smlFile, true,
-                                           attachmentFilenames, attachmentNames, attachmentDescriptions, 
-                                           attachmentMimeTypes);
-        if (entry != null)
-            mgr.getArchive().regenerateIndex();
-    }
-    
-    /**
-     * Create a new entry, storing it into the blogManager's archive and incrementing the 
-     * blog's "most recent id" setting.  This does not however regenerate the manager's index.
-     *
-     * @param blogHashStr base64(SHA256(blog public key))
-     * @param tags list of tags/categories to post under (String elements
-     * @param entryIdDef NEXT (for next entry id for the blog, or midnight of the current day),
-     *                   NOW (current time), or an explicit entry id
-     * @param entryKeyDef session key under which the entry should be encrypted
-     * @param smlFilename file in which the sml entry is to be found
-     * @param storeLocal if true, should this entry be stored in the mgr.getArchive()
-     * @param attachmentFilenames list of filenames for attachments to load
-     * @param attachmentNames list of names to use for the given attachments
-     * @param attachmentDescriptions list of descriptions for the given attachments
-     * @param attachmentMimeTypes list of mime types to use for the given attachments
-     * @return blog URI posted, or null
-     */
-    public static EntryContainer createEntry(I2PAppContext ctx, BlogManager mgr, String blogHashStr, List tags, 
-                                             String entryIdDef, String entryKeyDef, String smlFilename, boolean storeLocal,
-                                             List attachmentFilenames, List attachmentNames, 
-                                             List attachmentDescriptions, List attachmentMimeTypes) {
-        Hash blogHash = new Hash(Base64.decode(blogHashStr));
-        User user = mgr.getUser(blogHash);
-        long entryId = -1;
-        if ("NOW".equalsIgnoreCase(entryIdDef)) {
-            entryId = ctx.clock().now();
-        } else if ("NEXT".equalsIgnoreCase(entryIdDef) || (entryIdDef == null)) {
-            entryId = mgr.getNextBlogEntry(user);
-        } else {
-            try {
-                entryId = Long.parseLong(entryIdDef);
-            } catch (NumberFormatException nfe) {
-                nfe.printStackTrace();
-                return null;
-            }
-        }
-        String tagVals[] = new String[(tags != null ? tags.size() : 0)];
-        if (tags != null)
-            for (int i = 0; i < tags.size(); i++)
-                tagVals[i] = ((String)tags.get(i)).trim();
-        BlogURI uri = new BlogURI(blogHash, entryId);
-        BlogInfo blog = mgr.getArchive().getBlogInfo(uri);
-        if (blog == null) {
-            System.err.println("Blog does not exist: " + uri);
-            return null;
-        }
-        SigningPrivateKey key = mgr.getMyPrivateKey(blog);
-        
-        try {
-            byte smlData[] = read(smlFilename);
-            EntryContainer c = new EntryContainer(uri, tagVals, smlData);
-            if ( (attachmentFilenames != null) && 
-                 (attachmentFilenames.size() == attachmentNames.size()) && 
-                 (attachmentFilenames.size() == attachmentDescriptions.size()) && 
-                 (attachmentFilenames.size() == attachmentMimeTypes.size()) ) {
-                for (int i = 0; i < attachmentFilenames.size(); i++) {
-                    File attachmentFile = new File((String)attachmentFilenames.get(i));
-                    String name = (String)attachmentNames.get(i);
-                    String descr = (String)attachmentDescriptions.get(i);
-                    String mimetype = (String)attachmentMimeTypes.get(i);
-                    c.addAttachment(read(attachmentFile.getAbsolutePath()), name, descr, mimetype);
-                }
-            }
-            SessionKey entryKey = null;
-            if ( (entryKeyDef != null) && (entryKeyDef.trim().length() > 0) && (!"NONE".equalsIgnoreCase(entryKeyDef)) )
-                entryKey = new SessionKey(Base64.decode(entryKeyDef));
-            c.seal(ctx, key, entryKey);
-            if (storeLocal) {
-                boolean ok = mgr.getArchive().storeEntry(c);
-                //System.out.println("Blog entry created: " + c+ "? " + ok);
-                if (!ok) {
-                    System.err.println("Error: store failed");
-                    return null;
-                }
-            }
-            user.setMostRecentEntry(uri.getEntryId());
-            mgr.storeUser(user); // saves even if !user.getAuthenticated()
-            return c;
-        } catch (IOException ioe) {
-            ioe.printStackTrace();
-            return null;
-        }
-    }
-    
-    private static final byte[] read(String file) throws IOException {
-        File f = new File(file);
-        FileInputStream in = new FileInputStream(f);
-        byte rv[] = new byte[(int)f.length()];
-        if (rv.length != DataHelper.read(in, rv))
-            throw new IOException("File not read completely");
-        return rv;
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/CLIPost.java b/apps/syndie/java/src/net/i2p/syndie/CLIPost.java
deleted file mode 100644
index 59f0bb6a755dc4ddfaa6a542ac675e0571fcadbc..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/CLIPost.java
+++ /dev/null
@@ -1,218 +0,0 @@
-package net.i2p.syndie;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
-
-import net.i2p.I2PAppContext;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.util.EepPost;
-
-/**
- * Simple CLI to post an entry.
- * 
- */
-public class CLIPost {
-    public static final String USAGE = "Usage: \"" + CLIPost.class.getName() + " [args]\", where args are:"
-        + "\n  --syndieDir $syndieRootDir  // syndie root dir, under which syndie.config exists"
-        + "\n  --blog $blogHash            // base64 of the blog's key"
-        + "\n  --sml $smlFile              // file with the SML entry"
-        + "\n  [--importurl ($url|none)]   // defaults to http://localhost:7657/syndie/import.jsp"
-        + "\n  [--proxyhost $hostname]     // HTTP proxy host for sending the data to the import URL"
-        + "\n  [--proxyport $portnum]      // HTTP proxy port for sending the data to the import URL"
-        + "\n  [--storelocal (true|false)] // should it be stored directly with the file system"
-        + "\n                              // (false by default, since its stored locally via importurl)"
-        + "\n  [--entryId ($num|next|now)] // entryId to use: explicit, the blog's next (default), or timestamp"
-        + "\n  [--attachment$N $file $name $desc $type]"
-        + "\n                              // Nth file / suggested name / description / mime type";
-    
-    public static void main(String args[]) {
-        String rootDir = getArg(args, "syndieDir");
-        String hashStr = getArg(args, "blog");
-        String smlFile = getArg(args, "sml");
-        if ( (rootDir == null) || (hashStr == null) || (smlFile == null) ) {
-            System.err.println(USAGE);
-            return;
-        }
-        
-        String url = getArg(args, "importurl");
-        String entryIdDef = getArg(args, "entryId");
-        
-        List attachmentFilenames = new ArrayList();
-        List attachmentNames = new ArrayList();
-        List attachmentDescriptions = new ArrayList();
-        List attachmentMimeTypes = new ArrayList();
-        while (true) {
-            // --attachment$N $file $name $desc $type]
-            String file = getAttachmentParam(args, attachmentFilenames.size(), 0);
-            String name = getAttachmentParam(args, attachmentFilenames.size(), 1);
-            String desc = getAttachmentParam(args, attachmentFilenames.size(), 2);
-            String type = getAttachmentParam(args, attachmentFilenames.size(), 3);
-            if ( (file != null) && (name != null) && (desc != null) && (type != null) ) {
-                attachmentFilenames.add(file);
-                attachmentNames.add(name);
-                attachmentDescriptions.add(desc);
-                attachmentMimeTypes.add(type);
-            } else {
-                break;
-            }
-        }
-        
-        List tags = readTags(smlFile);
-        
-        // don't support the entry key stuff yet...
-        String entryKeyDef = null; //args[5];
-        
-        String loc = getArg(args, "storelocal");
-        boolean storeLocal = false;
-        if (loc != null)
-            storeLocal = Boolean.valueOf(loc).booleanValue();
-        
-        if (!storeLocal && "none".equalsIgnoreCase(url)) {
-            System.err.println("You need to post it somewhere, so either specify \"--storelocal true\"");
-            System.err.println("or don't specify \"--importurl none\"");
-            return;
-        }
-        
-        I2PAppContext ctx = I2PAppContext.getGlobalContext();
-        BlogManager mgr = new BlogManager(ctx, rootDir, false);
-        EntryContainer entry = CLI.createEntry(ctx, mgr, hashStr, tags, entryIdDef, entryKeyDef, smlFile, storeLocal, 
-                                               attachmentFilenames, attachmentNames, attachmentDescriptions, 
-                                               attachmentMimeTypes);
-        if (entry != null) {
-            if (storeLocal)
-                mgr.getArchive().regenerateIndex();
-            if (!("none".equalsIgnoreCase(url))) {
-                if ( (url == null) || (url.trim().length() <= 0) )
-                    url = "http://localhost:7657/syndie/import.jsp";
-
-                // now send it to the import URL
-                BlogInfo info = mgr.getArchive().getBlogInfo(entry.getURI().getKeyHash());
-                File fMeta = null;
-                File fData = null;
-
-                try {
-                    fMeta = File.createTempFile("cli", ".snm", mgr.getTempDir());
-                    fData = File.createTempFile("cli", ".snd", mgr.getTempDir());
-                    FileOutputStream out = new FileOutputStream(fMeta);
-                    info.write(out);
-                    out.close();
-                    out = new FileOutputStream(fData);
-                    entry.write(out, true);
-                    out.close();
-                    fMeta.deleteOnExit();
-                    fData.deleteOnExit();
-                } catch (IOException ioe) {
-                    System.err.println("Error writing temp files: " + ioe.getMessage());
-                    return;
-                }
-
-                Map uploads = new HashMap(2);
-                uploads.put("blogmeta0", fMeta);
-                uploads.put("blogpost0", fData);
-
-                String proxyHost = getArg(args, "proxyhost");
-                String proxyPortStr = getArg(args, "proxyport");
-                int proxyPort = -1;
-                if (proxyPortStr != null) 
-                    try { proxyPort = Integer.parseInt(proxyPortStr); } catch (NumberFormatException nfe) { }
-
-                OnCompletion job = new OnCompletion();
-                EepPost post = new EepPost();
-                post.postFiles(url, (proxyPort > 0 ? proxyHost : null), proxyPort, uploads, job);
-                boolean posted = job.waitForCompletion(30*1000);
-                if (posted)
-                    System.out.println("Posted successfully: " + entry.getURI().toString());
-                else
-                    System.out.println("Posting failed");
-            } else if (storeLocal) {
-                System.out.println("Store local successfully: " + entry.getURI().toString());
-            } else {
-                // foo
-            }
-        } else {
-            System.err.println("Error creating the blog entry");
-        }
-    }
-    
-    private static class OnCompletion implements Runnable {
-        private boolean _complete;
-        public OnCompletion() { _complete = false; }
-        public void run() { 
-            _complete = true; 
-            synchronized (OnCompletion.this) { 
-                OnCompletion.this.notifyAll();
-            }
-        }
-        public boolean waitForCompletion(long max) {
-            long end = max + System.currentTimeMillis();
-            while (!_complete) {
-                long now = System.currentTimeMillis();
-                if (now >= end)
-                    return false;
-                try {
-                    synchronized (OnCompletion.this) {
-                        OnCompletion.this.wait(end-now);
-                    }
-                } catch (InterruptedException ie) {}
-            }
-            return true;
-        }
-    }
-
-    private static String getArg(String args[], String param) {
-        if (args != null) 
-            for (int i = 0; i + 1< args.length; i++)
-                if (args[i].equalsIgnoreCase("--"+param))
-                    return args[i+1];
-        return null;
-    }
-    private static String getAttachmentParam(String args[], int attachmentNum, int paramNum) {
-        if (args != null) 
-            for (int i = 0; i + 4 < args.length; i++)
-                if (args[i].equalsIgnoreCase("--attachment"+attachmentNum))
-                    return args[i+1+paramNum];
-        return null;
-    }
-    
-    private static List readTags(String smlFile) {
-        BufferedReader in = null;
-        try {
-            in = new BufferedReader(new InputStreamReader(new FileInputStream(smlFile), "UTF-8"));
-            String line = null;
-            while ( (line = in.readLine()) != null) {
-                if (line.length() <= 0)
-                    return new ArrayList();
-                else if (line.startsWith("Tags:"))
-                    return parseTags(line.substring("Tags:".length()));
-            }
-            return null;
-        } catch (IOException ioe) {
-            return new ArrayList();
-        } finally {
-            if (in != null) try { in.close(); } catch (IOException ioe) {}
-        }
-    }
-    
-    private static List parseTags(String tags) {
-        if (tags == null) 
-            return new ArrayList();
-        StringTokenizer tok = new StringTokenizer(tags, " ,\t\n");
-        List rv = new ArrayList();
-        while (tok.hasMoreTokens()) {
-            String cur = tok.nextToken().trim();
-            if (cur.length() > 0)
-                rv.add(cur);
-        }
-        return rv;
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/CachedEntry.java b/apps/syndie/java/src/net/i2p/syndie/CachedEntry.java
deleted file mode 100644
index b1fdaa811144571cfe2f0a0643f768c79ef64814..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/CachedEntry.java
+++ /dev/null
@@ -1,268 +0,0 @@
-package net.i2p.syndie;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-
-import net.i2p.I2PAppContext;
-import net.i2p.data.Base64;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Hash;
-import net.i2p.data.SessionKey;
-import net.i2p.data.Signature;
-import net.i2p.syndie.data.Attachment;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.Entry;
-import net.i2p.syndie.data.EntryContainer;
-
-/**
- * Lazy loading wrapper for an entry, pulling data out of a cached & extracted dir,
- * rather than dealing with the crypto, zip, etc.
- *
- */
-class CachedEntry extends EntryContainer {
-    private File _entryDir;
-    
-    private int _format;
-    private int _size;
-    private BlogURI _blog;
-    private Properties _headers;
-    private Entry _entry;
-    private Attachment _attachments[];
-    
-    public CachedEntry(File entryDir) throws IOException {
-        _entryDir = entryDir;
-        importMeta();
-        _entry = new CachedEntryDetails();
-        _attachments = null;
-    }
-    
-    public boolean isValid() { 
-        return (_entry != null) && (_blog != null);
-    }
-    
-    // always available, loaded from meta
-    public int getFormat() { return _format; }
-    public BlogURI getURI() { return _blog; }
-    public int getCompleteSize() { return _size; }
-    
-    // dont need to override it, as it works off getHeader
-    //public String[] getTags() { return super.getTags(); }
-    
-    public Entry getEntry() { return _entry; }
-    public Attachment[] getAttachments() {
-        importAttachments();
-        return _attachments; 
-    }
-    public String getHeader(String key) {
-        importHeaders();
-        return _headers.getProperty(key);
-    }
-    
-    public String toString() { return getURI().toString(); }
-    public boolean verifySignature(I2PAppContext ctx, BlogInfo info) { return true; }
-    
-    // not supported...
-    public void parseRawData(I2PAppContext ctx) throws IOException { 
-        throw new IllegalStateException("Not supported on cached entries"); 
-    }
-    public void parseRawData(I2PAppContext ctx, SessionKey zipKey) throws IOException {
-        throw new IllegalStateException("Not supported on cached entries"); 
-    }
-    public void setHeader(String name, String val) {
-        throw new IllegalStateException("Not supported on cached entries"); 
-    }
-    public void addAttachment(byte data[], String name, String description, String mimeType) {
-        throw new IllegalStateException("Not supported on cached entries"); 
-    }
-    public void write(OutputStream out, boolean includeRealSignature) throws IOException { 
-        throw new IllegalStateException("Not supported on cached entries"); 
-    }
-    public Signature getSignature() { 
-        throw new IllegalStateException("Not supported on cached entries"); 
-    }
-    
-    // now the actual lazy loading code
-    private void importMeta() throws IOException {
-        Properties meta = readProps(new File(_entryDir, EntryExtractor.META));
-        _format = getInt(meta, "format");
-        _size = getInt(meta, "size");
-        _blog = new BlogURI(new Hash(Base64.decode(meta.getProperty("blog"))), getLong(meta, "entry"));
-    }
-    
-    private Properties importHeaders() {
-        if (_headers == null) {
-            try {
-                _headers = readProps(new File(_entryDir, EntryExtractor.HEADERS));
-            } catch (IOException ioe) {
-                ioe.printStackTrace();
-                _headers = new Properties();
-            }
-        }
-        return _headers;
-    }
-    
-    private void importAttachments() {
-        if (_attachments == null) {
-            List attachments = new ArrayList();
-            int i = 0;
-            while (true) {
-                File meta = new File(_entryDir, EntryExtractor.ATTACHMENT_PREFIX + i + EntryExtractor.ATTACHMENT_META_SUFFIX);
-                if (meta.exists())
-                    attachments.add(new CachedAttachment(i, meta));
-                else
-                    break;
-                i++;
-            }
-            Attachment a[] = new Attachment[attachments.size()];
-            for (i = 0; i < a.length; i++)
-                a[i] = (Attachment)attachments.get(i);
-            _attachments = a;
-        }
-        return;
-    }
-    
-    private static Properties readProps(File propsFile) throws IOException {
-        Properties rv = new Properties();
-        BufferedReader in = null;
-        try {
-            in = new BufferedReader(new InputStreamReader(new FileInputStream(propsFile), "UTF-8"));
-            String line = null;
-            while ( (line = in.readLine()) != null) {
-                int split = line.indexOf('=');
-                if ( (split <= 0) || (split >= line.length()) ) continue;
-                rv.setProperty(line.substring(0, split).trim(), line.substring(split+1).trim());
-            }
-        } finally {
-            if (in != null) try { in.close(); } catch (IOException ioe) {}
-        }
-        return rv;
-    }
-    
-    private static final int getInt(Properties props, String key) {
-        String val = props.getProperty(key);
-        try { return Integer.parseInt(val); } catch (NumberFormatException nfe) {}
-        return -1;
-    }
-    private static final long getLong(Properties props, String key) {
-        String val = props.getProperty(key);
-        try { return Long.parseLong(val); } catch (NumberFormatException nfe) {}
-        return -1l;
-    }
-
-    
-    private class CachedEntryDetails extends Entry {
-        private String _text;
-        public CachedEntryDetails() {
-            super(null);
-        }
-        public String getText() { 
-            importText();
-            return _text; 
-        }
-        private void importText() {
-            if (_text == null) {
-                InputStream in = null;
-                try {
-                    File f = new File(_entryDir, EntryExtractor.ENTRY);
-                    byte buf[] = new byte[(int)f.length()]; // hmm
-                    in = new FileInputStream(f);
-                    int read = DataHelper.read(in, buf);
-                    if (read != buf.length) throw new IOException("read: " + read + " file size: " + buf.length + " for " + f.getPath());
-                    _text = DataHelper.getUTF8(buf);
-                } catch (IOException ioe) {
-                    ioe.printStackTrace();
-                } finally {
-                    if (in != null) try { in.close(); } catch (IOException ioe) {}
-                }
-            }
-        }
-    }
-    
-    private class CachedAttachment extends Attachment {
-        private int _attachmentId;
-        private File _metaFile;
-        private Properties _attachmentHeaders;
-        private int _dataSize;
-        
-        public CachedAttachment(int id, File meta) {
-            super(null, null);
-            _attachmentId = id;
-            _metaFile = meta;
-            _attachmentHeaders = null;
-        }
-
-        public int getDataLength() { 
-            importAttachmentHeaders();
-            return _dataSize; 
-        }
-        
-        public byte[] getData() { 
-            throw new IllegalStateException("Not supported on cached entries"); 
-        }
-        public InputStream getDataStream() throws IOException { 
-            String name = EntryExtractor.ATTACHMENT_PREFIX + _attachmentId + EntryExtractor.ATTACHMENT_DATA_SUFFIX;
-            File f = new File(_entryDir, name);
-            return new FileInputStream(f); 
-        }
-
-        public byte[] getRawMetadata() { 
-            throw new IllegalStateException("Not supported on cached entries"); 
-        }
-
-        public String getMeta(String key) { 
-            importAttachmentHeaders();
-            return _attachmentHeaders.getProperty(key);
-        }
-
-        //public String getName() { return getMeta(NAME); }
-        //public String getDescription() { return getMeta(DESCRIPTION); }
-        //public String getMimeType() { return getMeta(MIMETYPE); }
-
-        public void setMeta(String key, String val) {
-            throw new IllegalStateException("Not supported on cached entries"); 
-        }
-
-        public Map getMeta() {
-            importAttachmentHeaders();
-            return _attachmentHeaders;
-        }
-
-        public String toString() { 
-            importAttachmentHeaders();
-            int len = _dataSize;
-            return getName() 
-                   + (getDescription() != null ? ": " + getDescription() : "") 
-                   + (getMimeType() != null ? ", type: " + getMimeType() : "") 
-                   + ", size: " + len; 
-        }
-
-        private void importAttachmentHeaders() {
-            if (_attachmentHeaders == null) {
-                try {
-                    Properties props = readProps(_metaFile);
-                    String sz = (String)props.remove(EntryExtractor.ATTACHMENT_DATA_SIZE);
-                    if (sz != null) {
-                        try { 
-                            _dataSize = Integer.parseInt(sz);
-                        } catch (NumberFormatException nfe) {}
-                    }
-
-                    _attachmentHeaders = props;
-                } catch (IOException ioe) {
-                    ioe.printStackTrace();
-                    _attachmentHeaders = new Properties();
-                }
-            }
-        }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/EntryExtractor.java b/apps/syndie/java/src/net/i2p/syndie/EntryExtractor.java
deleted file mode 100644
index 179a35f9e3eaa9b2f33e6c76b68318b69ed4815d..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/EntryExtractor.java
+++ /dev/null
@@ -1,150 +0,0 @@
-package net.i2p.syndie;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Iterator;
-import java.util.Map;
-
-import net.i2p.I2PAppContext;
-import net.i2p.data.DataHelper;
-import net.i2p.data.SessionKey;
-import net.i2p.syndie.data.Attachment;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.Entry;
-import net.i2p.syndie.data.EntryContainer;
-
-/**
- * To cut down on unnecessary IO/cpu load, extract entries onto the disk for 
- * faster access later.  Individual entries are stored in subdirectories based on
- * their name - $archiveDir/$blogDir/$entryId.snd extracts its files into various
- * files under $cacheDir/$blogDir/$entryId/:
- *  headers.txt: name=value pairs for attributes of the entry container itself
- *  info.txt: name=value pairs for implicit attributes of the container (blog, id, format, size)
- *  entry.sml: raw sml file
- *  attachmentN_data.dat: raw binary data for attachment N
- *  attachmentN_meta.dat: name=value pairs for attributes of attachment N
- * 
- */
-public class EntryExtractor {
-    private I2PAppContext _context;
-    
-    static final String HEADERS = "headers.txt";
-    static final String META = "meta.txt";
-    static final String ENTRY = "entry.sml";
-    static final String ATTACHMENT_PREFIX = "attachment";
-    static final String ATTACHMENT_DATA_SUFFIX = "_data.dat";
-    static final String ATTACHMENT_META_SUFFIX = "_meta.txt";
-    static final String ATTACHMENT_DATA_SIZE = "EntryExtractor__dataSize";
-    
-    public EntryExtractor(I2PAppContext context) {
-        _context = context;
-    }
-    
-    public boolean extract(File entryFile, File entryDir, SessionKey entryKey, BlogInfo info) throws IOException {
-        EntryContainer entry = new EntryContainer();
-        FileInputStream in = null;
-        try {
-            in = new FileInputStream(entryFile);
-            entry.load(in);
-        } finally {
-            if (in != null) try { in.close(); } catch (IOException ioe) {}
-        }
-        boolean ok = entry.verifySignature(_context, info);
-        if (!ok) {
-            return false;
-        } else {
-            entry.setCompleteSize((int)entryFile.length());
-            if (entryKey != null)
-                entry.parseRawData(_context, entryKey);
-            else
-                entry.parseRawData(_context);
-            extract(entry, entryDir);
-            return true;
-        }
-    }
-    
-    public void extract(EntryContainer entry, File entryDir) throws IOException {
-        extractEntry(entry, entryDir);
-        extractHeaders(entry, entryDir);
-        extractMeta(entry, entryDir);
-        Attachment attachments[] = entry.getAttachments();
-        if (attachments != null) {
-            for (int i = 0; i < attachments.length; i++) {
-                extractAttachmentData(i, attachments[i], entryDir);
-                extractAttachmentMetadata(i, attachments[i], entryDir);
-            }
-        }
-    }
-    private void extractHeaders(EntryContainer entry, File entryDir) throws IOException {
-        FileOutputStream out = null;
-        try {
-            out = new FileOutputStream(new File(entryDir, HEADERS));
-            Map headers = entry.getHeaders();
-            for (Iterator iter = headers.keySet().iterator(); iter.hasNext(); ) {
-                String k = (String)iter.next();
-                String v = (String)headers.get(k);
-                out.write(DataHelper.getUTF8(k.trim() + '=' + v.trim() + '\n'));
-            }
-        } finally {
-            out.close();
-        }
-    }
-    private void extractMeta(EntryContainer entry, File entryDir) throws IOException {
-        FileOutputStream out = null;
-        try {
-            out = new FileOutputStream(new File(entryDir, META));
-            out.write(DataHelper.getUTF8("format=" + entry.getFormat() + '\n'));
-            out.write(DataHelper.getUTF8("size=" + entry.getCompleteSize() + '\n'));
-            out.write(DataHelper.getUTF8("blog=" + entry.getURI().getKeyHash().toBase64() + '\n'));
-            out.write(DataHelper.getUTF8("entry=" + entry.getURI().getEntryId() + '\n'));
-        } finally {
-            out.close();
-        }
-    }
-    private void extractEntry(EntryContainer entry, File entryDir) throws IOException {
-        Entry e = entry.getEntry();
-        if (e == null) throw new IOException("Entry is null");
-        String text = e.getText();
-        if (text == null) throw new IOException("Entry text is null");
-        FileOutputStream out = null;
-        try {
-            out = new FileOutputStream(new File(entryDir, ENTRY));
-            out.write(DataHelper.getUTF8(text));
-        } finally {
-            out.close();
-        }
-    }
-    private void extractAttachmentData(int num, Attachment attachment, File entryDir) throws IOException {
-        FileOutputStream out = null;
-        try {
-            out = new FileOutputStream(new File(entryDir, ATTACHMENT_PREFIX + num + ATTACHMENT_DATA_SUFFIX));
-            //out.write(attachment.getData());
-            InputStream data = attachment.getDataStream();
-            byte buf[] = new byte[1024];
-            int read = 0;
-            while ( (read = data.read(buf)) != -1) 
-                out.write(buf, 0, read);
-            data.close();
-        } finally {
-            out.close();
-        }
-    }
-    private void extractAttachmentMetadata(int num, Attachment attachment, File entryDir) throws IOException {
-        FileOutputStream out = null;
-        try {
-            out = new FileOutputStream(new File(entryDir, ATTACHMENT_PREFIX + num + ATTACHMENT_META_SUFFIX));
-            Map meta = attachment.getMeta();
-            for (Iterator iter = meta.keySet().iterator(); iter.hasNext(); ) {
-                String k = (String)iter.next();
-                String v = (String)meta.get(k);
-                out.write(DataHelper.getUTF8(k + '=' + v + '\n'));
-            }
-            out.write(DataHelper.getUTF8(ATTACHMENT_DATA_SIZE + '=' + attachment.getDataLength()));
-        } finally {
-            out.close();
-        }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/HeaderReceiver.java b/apps/syndie/java/src/net/i2p/syndie/HeaderReceiver.java
deleted file mode 100644
index 69c1ce1f86c7d9e1c35fa751cf685d0c499e3a02..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/HeaderReceiver.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package net.i2p.syndie;
-
-import java.util.List;
-import java.util.Properties;
-
-import net.i2p.syndie.sml.SMLParser;
-
-public class HeaderReceiver implements SMLParser.EventReceiver {
-    private Properties _headers;
-    public HeaderReceiver() { _headers = null; }
-    public String getHeader(String name) { return (_headers != null ? _headers.getProperty(name) : null); }
-    public void receiveHeader(String header, String value) { 
-        if (_headers == null) _headers = new Properties();
-        _headers.setProperty(header, value);
-    }
-
-    public void receiveAddress(String name, String schema, String protocol, String location, String anchorText) {}
-    public void receiveArchive(String name, String description, String locationSchema, String location, String postingKey, String anchorText) {}
-    public void receiveAttachment(int id, int thumbnail, String anchorText) {}
-    public void receiveBegin() {}
-    public void receiveBlog(String name, String blogKeyHash, String blogPath, long blogEntryId, List blogArchiveLocations, String anchorText) {}
-    public void receiveBold(String text) {}
-    public void receiveCode(String text, String codeLocationSchema, String codeLocation) {}
-    public void receiveCut(String summaryText) {}
-    public void receiveEnd() {}
-    public void receiveGT() {}
-    public void receiveH1(String text) {}
-    public void receiveH2(String text) {}
-    public void receiveH3(String text) {}
-    public void receiveH4(String text) {}
-    public void receiveH5(String text) {}
-    public void receiveHR() {}
-    public void receiveHeaderEnd() {}
-    public void receiveImage(String alternateText, int attachmentId) {}
-    public void receiveItalic(String text) {}
-    public void receiveLT() {}
-    public void receiveLeftBracket() {}
-    public void receiveLink(String schema, String location, String text) {}
-    public void receiveNewline() {}
-    public void receivePlain(String text) {}
-    public void receivePre(String text) {}
-    public void receiveQuote(String text, String whoQuoted, String quoteLocationSchema, String quoteLocation) {}
-    public void receiveRightBracket() {}
-    public void receiveUnderline(String text) {}
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/NewestEntryFirstComparator.java b/apps/syndie/java/src/net/i2p/syndie/NewestEntryFirstComparator.java
deleted file mode 100644
index aeb7ccf4575778542191667c444e1f08b91c7c4a..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/NewestEntryFirstComparator.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package net.i2p.syndie;
-
-import java.util.Comparator;
-
-import net.i2p.data.DataHelper;
-import net.i2p.syndie.data.BlogURI;
-
-/** sort BlogURI instances with the highest entryId first */
-public class NewestEntryFirstComparator implements Comparator {
-    public int compare(Object lhs, Object rhs) {
-        BlogURI left = (BlogURI)lhs;
-        BlogURI right = (BlogURI)rhs;
-        if (left.getEntryId() > right.getEntryId()) {
-            return -1;
-        } else if (left.getEntryId() == right.getEntryId()) {
-            return DataHelper.compareTo(left.getKeyHash().getData(), right.getKeyHash().getData());
-        } else {
-            return 1;
-        }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/NewestNodeFirstComparator.java b/apps/syndie/java/src/net/i2p/syndie/NewestNodeFirstComparator.java
deleted file mode 100644
index 50ba5bac00432dbed14ef61d1419083999b49af2..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/NewestNodeFirstComparator.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package net.i2p.syndie;
-
-import java.util.Comparator;
-
-import net.i2p.data.DataHelper;
-
-/** sort ThreadNodeImpl instances with the highest entryId first */
-public class NewestNodeFirstComparator implements Comparator {
-    public int compare(Object lhs, Object rhs) {
-        ThreadNodeImpl left = (ThreadNodeImpl)lhs;
-        ThreadNodeImpl right = (ThreadNodeImpl)rhs;
-        long l = left.getMostRecentPostDate();
-        long r = right.getMostRecentPostDate();
-        if (l > r) { 
-            return -1;
-        } else if (l == r) {
-            // ok, the newest responses match, so lets fall back and compare the roots themselves
-            l = left.getEntry().getEntryId();
-            r = right.getEntry().getEntryId();
-            if (l > r) {
-                return -1;
-            } else if (l == r) {
-                return DataHelper.compareTo(left.getEntry().getKeyHash().getData(), right.getEntry().getKeyHash().getData());
-            } else {
-                return 1;
-            }
-        } else {
-            return 1;
-        }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/Sucker.java b/apps/syndie/java/src/net/i2p/syndie/Sucker.java
deleted file mode 100644
index e399392a8990dc637f2798f5f36488a51c6c3e13..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/Sucker.java
+++ /dev/null
@@ -1,995 +0,0 @@
-package net.i2p.syndie;
-
-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.net.MalformedURLException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.List;
-
-import net.i2p.I2PAppContext;
-import net.i2p.data.Base64;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Hash;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.util.EepGet;
-import net.i2p.util.Log;
-
-import com.sun.syndication.feed.synd.SyndCategory;
-import com.sun.syndication.feed.synd.SyndContent;
-import com.sun.syndication.feed.synd.SyndEnclosure;
-import com.sun.syndication.feed.synd.SyndEntry;
-import com.sun.syndication.feed.synd.SyndFeed;
-import com.sun.syndication.io.FeedException;
-import com.sun.syndication.io.SyndFeedInput;
-import com.sun.syndication.io.XmlReader;
-
-/**
- *
- * todo:
- * - factor out the parsing / formatting / posting to let the sucker pull in arbitrary HTML pages
- *   (importing the images and SMLizing some stuff)
- * - push the posts out to a remote syndie instance too
- */
-public class Sucker {
-    private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(Sucker.class);
-    private SuckerState _state;
-
-    public Sucker() {}
-    
-    public Sucker(String[] strings) throws IllegalArgumentException {
-        SuckerState state = new SuckerState();
-        state.pushToSyndie=true;
-        state.urlToLoad = strings[0];
-        state.blog = strings[1];
-        state.feedTag = strings[2];
-        state.outputDir = "blog-"+state.blog;
-        try {
-            state.historyPath=BlogManager.instance().getRootDir().getCanonicalPath()+"/rss.history";
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-        state.proxyPort = BlogManager.instance().getDefaultProxyPort();
-        state.proxyHost = BlogManager.instance().getDefaultProxyHost();
-
-        state.bm = BlogManager.instance();
-        Hash blogHash = new Hash();
-        try {
-            blogHash.fromBase64(state.blog);
-        } catch (DataFormatException e1) {
-            throw new IllegalArgumentException("ooh, bad $blog");
-        }
-     
-        state.user = state.bm.getUser(blogHash);
-        if(state.user==null)
-            throw new IllegalArgumentException("wtf, user==null? hash:"+blogHash);
-        state.history = new ArrayList();
-        _state = state;
-    }
-
-    public boolean parseArgs(String args[]) {
-        for (int i = 0; i < args.length; i++) {
-            if ("--load".equals(args[i]))
-                _state.urlToLoad = args[++i];
-            if ("--outputdir".equals(args[i]))
-                _state.outputDir = args[++i];
-            if ("--history".equals(args[i]))
-                _state.historyPath = args[++i];
-            if ("--tag".equals(args[i]))
-                _state.feedTag = args[++i];
-            if ("--proxyhost".equals(args[i]))
-                _state.proxyHost = args[++i];
-            if ("--proxyport".equals(args[i]))
-                _state.proxyPort = args[++i];
-            if ("--exec".equals(args[i]))
-                _state.pushScript = args[++i];
-            if ("--importenclosures".equals(args[i]))
-                _state.importEnclosures= args[++i].equals("true");
-            if ("--importenrefs".equals(args[i]))
-                _state.importRefs= args[++i].equals("true");
-        }
-
-        // Cut ending '/' from outputDir
-        if (_state.outputDir.endsWith("/"))
-            _state.outputDir = _state.outputDir.substring(0, _state.outputDir.length() - 1);
-
-        if (_state.urlToLoad == null)
-            return false;
-
-        return true;
-    }
-    
-    /**
-     * Fetch urlToLoad and call convertToHtml() on any new entries.
-     * @return list of BlogURI entries posted, if any
-     */
-    public List suck() {
-        _state.entriesPosted = new ArrayList();
-        SyndFeed feed;
-        File fetched=null;
-        
-        _state.tempFiles = new ArrayList();
-        
-        // Find base url
-        int idx=_state.urlToLoad.lastIndexOf('/');
-        if(idx>0)
-            _state.baseUrl=_state.urlToLoad.substring(0,idx);
-        else
-            _state.baseUrl=_state.urlToLoad;
-
-        infoLog("Processing: "+_state.urlToLoad);
-        debugLog("Base url: "+_state.baseUrl);
-
-        //
-        try {
-            File lastIdFile=null;
-         
-            // Get next message number to use (for messageId in history only)
-            if(!_state.pushToSyndie) {
-                
-                lastIdFile = new File(_state.historyPath + ".lastId");
-                if (!lastIdFile.exists())
-                    lastIdFile.createNewFile();
-                
-                FileInputStream fis = null;
-                try {
-                    fis = new FileInputStream(lastIdFile);
-                    String number = readLine(fis);
-                    _state.messageNumber = Integer.parseInt(number);
-                } catch (NumberFormatException e) {
-                    _state.messageNumber = 0;
-                } finally {
-                    if (fis != null) try { fis.close(); } catch (IOException ioe) {}
-                }
-
-                // Create outputDir if missing
-                File f = new File(_state.outputDir);
-                f.mkdirs();
-            } else {
-                _state.messageNumber=_state.bm.getNextBlogEntry(_state.user);
-            }
-
-            _log.debug("message number: " + _state.messageNumber);
-            
-            _state.shouldProxy = false;
-            _state.proxyPortNum = -1;
-            if ( (_state.proxyHost != null) && (_state.proxyPort != null) ) {
-                try {
-                    _state.proxyPortNum = Integer.parseInt(_state.proxyPort);
-                    if (_state.proxyPortNum > 0)
-                        _state.shouldProxy = true;
-                } catch (NumberFormatException nfe) {
-                    nfe.printStackTrace();
-                }
-            }
-            
-            // fetch
-            int numRetries = 2;
-            fetched = File.createTempFile("sucker", ".fetch");
-            EepGet get = new EepGet(I2PAppContext.getGlobalContext(), _state.shouldProxy, _state.proxyHost, _state.proxyPortNum, 
-                                    numRetries, fetched.getAbsolutePath(), _state.urlToLoad);
-            SuckerFetchListener lsnr = new SuckerFetchListener();
-            get.addStatusListener(lsnr);
-            
-            _log.debug("fetching [" + _state.urlToLoad + "] / " + _state.shouldProxy + "/" + _state.proxyHost + "/" + _state.proxyHost);
-            
-            get.fetch();
-            _log.debug("fetched: " + get.getNotModified() + "/" + get.getETag());
-            boolean ok = lsnr.waitForSuccess();
-            if (!ok) {
-                _log.debug("success? " + ok);
-                System.err.println("Unable to retrieve the url [" + _state.urlToLoad + "] after " + numRetries + " tries.");
-                fetched.delete();
-                return _state.entriesPosted;
-            }
-            _log.debug("fetched successfully? " + ok);
-            if(get.getNotModified()) {
-                debugLog("not modified, saving network bytes from useless fetch");
-                fetched.delete();
-                return _state.entriesPosted;
-            }
-
-            // Build entry list from fetched rss file
-            SyndFeedInput input = new SyndFeedInput();
-            feed = input.build(new XmlReader(fetched));
-
-            List entries = feed.getEntries();
-
-            _log.debug("entries: " + entries.size());
-            
-            loadHistory();
-            
-            // Process list backwards to get syndie to display the 
-            // entries in the right order. (most recent at top)
-            List feedMessageIds = new ArrayList();
-            for (int i = entries.size()-1; i >= 0; i--) { 
-                SyndEntry e = (SyndEntry) entries.get(i);
-
-                _state.attachmentCounter=0;
-
-                if (_log.shouldLog(Log.DEBUG))
-                    _log.debug("Syndicate entry: " + e.getLink());
-
-                // Calculate messageId, and check if we have got the message already
-                String feedHash = sha1(_state.urlToLoad);
-                String itemHash = sha1(e.getTitle() + e.getDescription());
-                Date d = e.getPublishedDate();
-                String time;
-                if(d!=null)
-                    time = "" + d.getTime();
-                else
-                    time = "" + new Date().getTime();
-                String outputFileName = _state.outputDir + "/" + _state.messageNumber;
-                String messageId = feedHash + ":" + itemHash + ":" + time + ":" + outputFileName;
-                
-                // Make sure these messageIds get into the history file
-                feedMessageIds.add(messageId);
-                
-                // Check if we already have this
-                if (existsInHistory(_state, messageId))
-                    continue;
-                
-                infoLog("new: " + messageId);
-                
-                // process the new entry
-                processEntry(_state, e, time);
-            }
-            
-            // update history
-            pruneHistory(_state.urlToLoad, 42*10); // could use 0 if we were sure old entries never re-appear
-            Iterator iter = feedMessageIds.iterator();
-            while(iter.hasNext())
-            {
-                String newMessageId = (String)iter.next();
-                if(!existsInHistory(_state, newMessageId))
-                    addHistory(newMessageId); // add new message ids from current feed to history
-            }
-            storeHistory();
-
-            // call script if we don't just feed syndie
-            if(!_state.pushToSyndie) {
-                FileOutputStream fos = null;
-                try {
-                    fos = new FileOutputStream(lastIdFile);
-                    fos.write(("" + _state.messageNumber).getBytes());
-                } finally {
-                    if (fos != null) try { fos.close(); } catch (IOException ioe) {}
-                }
-            }
-            
-            _log.debug("done fetching");
-        } catch (MalformedURLException e) {
-            e.printStackTrace();
-        } catch (IllegalArgumentException e) {
-            e.printStackTrace();
-        } catch (FeedException e) {
-            e.printStackTrace();
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-        if(fetched!=null)
-            fetched.delete();
-        debugLog("Done.");
-        return _state.entriesPosted;
-    }
-
-    private void loadHistory() {        
-        try {
-            // Create historyFile if missing
-            _state.historyFile = new File(_state.historyPath);
-            if (!_state.historyFile.exists())
-                _state.historyFile.createNewFile();
-
-            FileInputStream is = new FileInputStream(_state.historyFile);
-            String s;
-            while((s=readLine(is))!=null)
-            {
-                addHistory(s);
-            }
-        } catch (FileNotFoundException e) {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        } catch (IOException e) {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        }
-        
-    }
-    
-    private boolean existsInHistory(SuckerState state, String messageId) {
-        int idx;
-        idx = messageId.lastIndexOf(":");
-        String lineToCompare = messageId.substring(0, idx-1);
-        idx = lineToCompare.lastIndexOf(":");
-        lineToCompare = lineToCompare.substring(0, idx-1);
-        Iterator iter = _state.history.iterator();
-        while(iter.hasNext())
-        {
-            String line = (String)iter.next();
-            idx = line.lastIndexOf(":");
-            if (idx < 0)
-                return false;
-            line = line.substring(0, idx-1);
-            idx = line.lastIndexOf(":");
-            if (idx < 0)
-                return false;
-            line = line.substring(0, idx-1);
-            if (line.equals(lineToCompare))
-                return true;
-        }
-        return false;
-    }
-
-    private void addHistory(String messageId) {
-        _state.history.add(messageId);
-    }
-    
-    private void pruneHistory(String url, int nrToKeep) {
-        int i=0;
-        String urlHash=sha1(url);
-        
-        // Count nr of entries containing url hash
-        Iterator iter = _state.history.iterator();
-        while(iter.hasNext())
-        {
-            String historyLine = (String) iter.next();
-            if(historyLine.startsWith(urlHash))
-            {
-                i++; 
-            }
-        }
-
-        // keep first nrToKeep entries
-        i = i - nrToKeep;
-        if(i>0)
-        {
-            iter = _state.history.iterator();
-            while(i>0 && iter.hasNext())
-            {
-                String historyLine = (String) iter.next();
-                if(historyLine.startsWith(urlHash))
-                {
-                    iter.remove();
-                    i--;
-                }
-            }
-        }
-    }
-
-    private void storeHistory() {
-        FileOutputStream hos = null;
-        try {
-            hos = new FileOutputStream(_state.historyFile, false);
-            Iterator iter = _state.history.iterator();
-            while(iter.hasNext())
-            {
-                String historyLine = (String) iter.next();
-                hos.write(historyLine.getBytes());
-                hos.write("\n".getBytes());
-            }
-        } catch (FileNotFoundException e) {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        } catch (IOException e) {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        } finally {
-            if (hos != null) try { hos.close(); } catch (IOException ioe) {}
-        }
-    }
-
-    public static void main(String[] args) {
-        Sucker sucker = new Sucker();
-        boolean ok = sucker.parseArgs(args);
-        if (!ok) {
-            System.out.println("sucker --load $urlToFeed \n"
-                    + "--proxyhost <host> \n" 
-                    + "--proxyport <port> \n"
-                    + "--importenclosures true \n" 
-                    + "--importrefs true \n"
-                    + "--tag feed \n" 
-                    + "--outputdir ./sucker_out \n"
-                    + "--exec pushscript.sh OUTPUTDIR UNIQUEID ENTRYTIMESTAMP \n"
-                    + "--history ./sucker.history");
-            System.exit(1);
-        }
-        
-        sucker.suck();
-    }
-
-    /**
-     * Call the specified script with "$outputDir $id and $time". 
-     */
-    private static boolean execPushScript(SuckerState state, String id, String time) {
-        try {
-            String cli = state.pushScript + " " + state.outputDir + " " + id + " " + time;
-            Process pushScript_proc = Runtime.getRuntime().exec(cli);
-
-            // get its output (your input) stream
-
-            InputStream ls_in = pushScript_proc.getInputStream();
-
-            try {
-                StringBuffer buf = new StringBuffer();
-                while (true) {
-                    boolean eof = DataHelper.readLine(ls_in, buf);
-                    if (buf.length() > 0) 
-                        infoLog(state.pushScript + ": " + buf.toString());
-                    buf.setLength(0);
-                    if (eof)
-                        break;
-                }
-            } catch (IOException e) {
-                return false;
-            }
-            try {
-                pushScript_proc.waitFor();
-                if(pushScript_proc.exitValue()==0)
-                    return true;
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-            }
-            return false;
-        } catch (IOException e1) {
-            System.err.println(e1);
-            return false;
-        }
-    }
-
-    /** 
-     * Converts the SyndEntry e to sml and fetches any images as attachments 
-     */ 
-    private static boolean processEntry(SuckerState state, SyndEntry e, String time) {
-        String subject;
-
-        state.stripNewlines=false;
-        
-        try {
-
-            String sml="";
-            subject=e.getTitle();
-            List cats = e.getCategories();
-            Iterator iter = cats.iterator();
-            String tags = state.feedTag;
-            while (iter.hasNext()) {
-                SyndCategory c = (SyndCategory) iter.next();
-                debugLog("Name: "+c.getName());
-                debugLog("uri:"+c.getTaxonomyUri());
-                String tag=c.getName();
-                tag=tag.replaceAll("[^a-zA-z.-_:]","_");
-                tags += "\t" + state.feedTag + "." + tag;
-            }
-
-            SyndContent content;
-
-            List l = e.getContents();
-            if(l!=null)
-            {
-                iter = l.iterator();
-                while(iter.hasNext())
-                {
-                    content = (SyndContent)iter.next();
-                    String c = content.getValue();
-                    debugLog("Content: "+c);
-                                  sml += htmlToSml(state, c);
-                                  sml += "\n";
-                }
-            }
-            
-            List enclosures = e.getEnclosures();
-            debugLog("Enclosures: " + enclosures.size());
-            for (int i = 0; i < enclosures.size(); i++) {
-                SyndEnclosure enc = (SyndEnclosure)enclosures.get(i);
-                String enclosureURL = enc.getUrl();
-                if (enclosureURL != null) {
-                    if (!enclosureURL.startsWith("http://")) {
-                        // e.g. postman's rss feed @ http://tracker.postman.i2p/rss.jsp has
-                        // baseUrl = http://tracker.postman.i2p
-                        // and enclosure URLs are /download.php?id=123&file=blah
-                        if (enclosureURL.startsWith("/") || state.baseUrl.endsWith("/"))
-                            enclosureURL = state.baseUrl + enclosureURL;
-                        else
-                            enclosureURL = state.baseUrl + '/' + enclosureURL;
-                    }   
-                    fetchAttachment(state, enclosureURL, enc.getType()); // fetches and adds to our streams
-                }
-            }
-            
-            String source=e.getLink(); //Uri();
-            if(!source.startsWith("http://"))
-                source=state.baseUrl+source;
-            sml += "[link schema=\"web\" location=\""+source+"\"]source[/link]\n";
-
-            if(state.pushToSyndie) {
-                debugLog("user.blog: "+state.user.getBlogStr());
-                debugLog("user.id: "+state.bm.getNextBlogEntry(state.user));
-                debugLog("subject: "+subject);
-                debugLog("tags: "+tags);
-                debugLog("sml: "+sml);
-                debugLog("");
-                BlogURI uri = state.bm.createBlogEntry(
-                        state.user, 
-                        false,
-                        subject, 
-                        tags, 
-                        null,
-                        sml, 
-                        state.fileNames, 
-                        state.fileStreams, 
-                        state.fileTypes);
-
-                if(uri==null) {
-                    errorLog("pushToSyndie failure.");
-                    return false;
-                } else {
-                    state.entriesPosted.add(uri);
-                    infoLog("pushToSyndie success, uri: "+uri.toString());
-                }
-            }
-            else
-            {
-                FileOutputStream fos;
-                fos = new FileOutputStream(state.messagePath);
-                sml=subject + "\nTags: " + tags + "\n\n" + sml;
-                fos.write(sml.getBytes());
-                if (state.pushScript != null) {
-                    if (!execPushScript(state, ""+state.messageNumber, time)) {
-                        errorLog("push script failed");
-                    } else {
-                        infoLog("push script success: nr "+state.messageNumber);
-                    }
-                }
-            }
-            state.messageNumber++;
-            deleteTempFiles(state);
-            return true;
-        } catch (FileNotFoundException e1) {
-            e1.printStackTrace();
-        } catch (IOException e2) {
-            e2.printStackTrace();
-        }
-        deleteTempFiles(state);
-        return false;
-    }
-
-    private static void deleteTempFiles(SuckerState state) {
-        Iterator iter = state.tempFiles.iterator();
-        while(iter.hasNext()) {
-            File tempFile = (File)iter.next();
-            tempFile.delete();
-        }
-    }
-
-    private static String htmlToSml(SuckerState state, String html) {
-
-        String sml="";
-        int i=0;
-
-        state.pendingEndLink=false;
-
-        while(i<html.length())
-        {
-            char c=html.charAt(i);
-            switch(c) {
-            case '<':
-                //log("html: "+html.substring(i));
-                
-                int tagLen = findTagLen(html.substring(i));
-                if(tagLen<=0) {
-                    // did not find anything that looks like tag, treat it like text
-                    sml+="&lt;";
-                    break;
-                }
-                //
-                String htmlTag = html.substring(i,i+tagLen);
-                
-                //log("htmlTag: "+htmlTag);
-                
-                String smlTag = htmlTagToSmlTag(state, htmlTag);
-                if(smlTag!=null) {
-                    sml+=smlTag;
-                    i+=tagLen;
-                    sml+=" "; 
-                    continue;
-                }
-                // Unrecognized tag, treat it as text
-                sml+="&lt;";
-                break;
-            case '\r':
-                if(!state.stripNewlines)
-                    sml+='\r';
-                break;
-            case '\n':
-                if(!state.stripNewlines)
-                    sml+='\n';
-                break;
-            case '[':
-                sml+="&#91;";
-                break;
-            case ']':
-                sml+="&#93;";
-                break;
-            default:
-                sml+=c;
-                break;
-            }
-            i++;
-        }
-        
-        return sml;
-    }
-
-    private static String htmlTagToSmlTag(SuckerState state, String htmlTag) {
-        final String ignoreTags[] = {
-                "span",
-                "tr",
-                "td",
-                "th",
-                "div",
-                "input",
-                "ul"
-        };
-        htmlTag = htmlTag.replaceAll("\\[","&#91;").replaceAll("\\]","&#93;");
-        String ret="";
-        String htmlTagLowerCase=htmlTag.toLowerCase();
-
-        if(htmlTagLowerCase.startsWith("<img"))
-        {
-            debugLog("Found image tag: "+htmlTag);
-            int a,b;
-            a=htmlTagLowerCase.indexOf("src=\"")+5;
-            b=a+1;
-            while(htmlTagLowerCase.charAt(b)!='\"')
-                b++;
-            String imageLink=htmlTag.substring(a,b);
-            
-            if(state.pendingEndLink) { // <a href="..."><img src="..."></a> -> [link][/link][img][/img]
-                ret="[/link]";
-                state.pendingEndLink=false;
-            }
-    
-            ret += "[img attachment=\""+""+ state.attachmentCounter +"\"]";
-            
-            a=htmlTagLowerCase.indexOf("alt=\"")+5;
-            if(a>=5)
-            {
-                b=a;
-                if(htmlTagLowerCase.charAt(b)!='\"') {
-                    while(htmlTagLowerCase.charAt(b)!='\"')
-                        b++;
-                    String altText=htmlTag.substring(a,b);
-                    ret+=altText;
-                }
-            }
-            
-            ret+="[/img]";
-            
-            if(!imageLink.startsWith("http://"))
-                imageLink=state.baseUrl+"/"+imageLink;
-            
-            fetchAttachment(state, imageLink);
-
-            debugLog("Converted to: "+ret);
-            
-            return ret;
-            
-        }
-        if(htmlTagLowerCase.startsWith("<a "))
-        {
-            debugLog("Found link tag: "+htmlTag);
-            int a,b;
-            
-            a=htmlTagLowerCase.indexOf("href=\"")+6;
-            b=a+1;
-            while ( (b < htmlTagLowerCase.length()) && (htmlTagLowerCase.charAt(b)!='\"') )
-                b++;
-            if (b >= htmlTagLowerCase.length())
-                return null; // abort the b0rked tag
-            String link=htmlTag.substring(a,b);
-            if(!link.startsWith("http://"))
-                link=state.baseUrl+"/"+link;
-            
-            String schema="web";
-            
-            ret += "[link schema=\""+schema+"\" location=\""+link+"\"]";
-            if(htmlTagLowerCase.endsWith("/>"))
-                ret += "[/link]";
-            else
-                state.pendingEndLink=true;
-        
-            debugLog("Converted to: "+ret);
-
-            return ret;
-        }
-        
-        if ("</a>".equals(htmlTagLowerCase)) {
-            if (state.pendingEndLink) {
-                state.pendingEndLink=false;
-                return "[/link]";
-            }
-            return "";
-        }
-        
-        if("<b>".equals(htmlTagLowerCase))
-            return "[b]";
-        if("</b>".equals(htmlTagLowerCase))
-            return "[/b]";
-        if("<i>".equals(htmlTagLowerCase))
-            return "[i]";
-        if("</i>".equals(htmlTagLowerCase))
-            return "[/i]";
-        if("<em>".equals(htmlTagLowerCase))
-            return "[i]";
-        if("</em>".equals(htmlTagLowerCase))
-            return "[/i]";
-        if("<strong>".equals(htmlTagLowerCase))
-            return "[b]";
-        if("</strong>".equals(htmlTagLowerCase))
-            return "[/b]";
-        if(htmlTagLowerCase.startsWith("<br")) {
-            state.stripNewlines=true;
-            return "\n";
-        }
-        if("<p>".equals(htmlTagLowerCase))
-            return "\n\n";
-        if("</p>".equals(htmlTagLowerCase))
-            return "";
-        if("<li>".equals(htmlTagLowerCase))
-            return "\n * ";
-        if("</li>".equals(htmlTagLowerCase))
-            return "";
-        if("</br>".equals(htmlTagLowerCase))
-            return "";
-        if(htmlTagLowerCase.startsWith("<hr"))
-            return "";
-        if("</img>".equals(htmlTagLowerCase))
-            return "";
-        if("</font>".equals(htmlTagLowerCase))
-            return "";
-        if("<blockquote>".equals(htmlTagLowerCase))
-            return "[quote]";
-        if("</blockquote>".equals(htmlTagLowerCase))
-            return "[/quote]";
-        if(htmlTagLowerCase.startsWith("<table") || "</table>".equals(htmlTagLowerCase)) // emulate table with hr :)
-            return "[hr][/hr]";
-        if(htmlTagLowerCase.startsWith("<font"))
-            return "";
-        
-
-        for(int i=0;i<ignoreTags.length;i++) {
-            String openTag = "<"+ignoreTags[i];
-            String closeTag = "</"+ignoreTags[i];
-            if(htmlTagLowerCase.startsWith(openTag))
-                return "";
-            if(htmlTagLowerCase.startsWith(closeTag))
-                return "";
-        }
-        
-        return null;
-    }
-
-    private static void fetchAttachment(SuckerState state, String link) { fetchAttachment(state, link, null); }
-    private static void fetchAttachment(SuckerState state, String link, String suggestedMimeType) {
-        
-        link=link.replaceAll("&amp;","&");
-        
-        infoLog("Fetch attachment from: "+link);
-        
-        File fetched;
-        if(state.pushToSyndie) {
-            try {
-                // perhaps specify a temp dir?
-                fetched = File.createTempFile("sucker",".attachment");
-                fetched.deleteOnExit();
-            } catch (IOException e) {
-                e.printStackTrace();
-                return;
-            }
-            state.tempFiles.add(fetched);
-        } else {
-            String attachmentPath = state.messagePath+"."+state.attachmentCounter;
-            fetched = new File(attachmentPath);
-        }
-        int numRetries = 2;
-        // we use eepGet, since it retries and doesn't leak DNS requests like URL does
-        EepGet get = new EepGet(I2PAppContext.getGlobalContext(), state.shouldProxy, state.proxyHost, state.proxyPortNum, 
-                                numRetries, fetched.getAbsolutePath(), link);
-        SuckerFetchListener lsnr = new SuckerFetchListener();
-        get.addStatusListener(lsnr);
-        get.fetch();
-        boolean ok = lsnr.waitForSuccess();
-        if (!ok) {
-            debugLog("Unable to retrieve the url [" + link + "] after " + numRetries + " tries.");
-            fetched.delete();
-            return;
-        }
-        state.tempFiles.add(fetched);
-        String filename=EepGet.suggestName(link);
-        String contentType = suggestedMimeType;
-        if (contentType == null)
-            contentType = get.getContentType();
-        if(contentType==null)
-            contentType="text/plain";
-        debugLog("successful fetch of filename "+filename + " suggested mime type [" + suggestedMimeType 
-                 + "], fetched mime type [" + get.getContentType() + "], final type [" + contentType + "]");
-        if(state.fileNames==null) state.fileNames = new ArrayList();
-        if(state.fileTypes==null) state.fileTypes = new ArrayList();
-        if(state.fileStreams==null) state.fileStreams = new ArrayList();
-        state.fileNames.add(filename);
-        state.fileTypes.add(contentType);
-        try {
-            state.fileStreams.add(new FileInputStream(fetched));
-        } catch (FileNotFoundException e) {
-            e.printStackTrace();
-        }
-        state.attachmentCounter++;
-    }
-
-    private static void errorLog(String string) {
-        if (_log.shouldLog(Log.ERROR))
-            _log.error(string);
-        //if(!pushToSyndie)
-        //    System.out.println(string);
-    }
-
-    private static void infoLog(String string) {
-        if (_log.shouldLog(Log.INFO))
-            _log.info(string);
-        //if(!pushToSyndie)
-        //    System.out.println(string);
-    }
-
-    private static void debugLog(String string) {
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug(string);
-        //if(!pushToSyndie)
-        //    System.out.println(string);
-    }
-
-    private static int findTagLen(String s) {
-        int i;
-        for(i=0;i<s.length();i++)
-        {
-            if(s.charAt(i)=='>')
-                return i+1;
-            if(s.charAt(i)=='"')
-            {
-                i++;
-                while(i<s.length() && s.charAt(i)!='"')
-                    i++;
-            }   
-        }
-        return -1;
-    }
-
-    private static String sha1(String s) {
-        try {
-            MessageDigest md = MessageDigest.getInstance("SHA");
-            md.update(s.getBytes());
-            byte[] buf = md.digest();
-            String ret = Base64.encode(buf);
-            return ret;
-        } catch (NoSuchAlgorithmException ex) {
-            ex.printStackTrace();
-        }
-        return null;
-    }
-
-    private static String readLine(FileInputStream in) {
-        StringBuffer sb = new StringBuffer();
-        int c = 0;
-        while (true) {
-            try {
-                c = in.read();
-            } catch (IOException e) {
-                e.printStackTrace();
-                return null;
-            }
-            if (c < 0)
-                return null;
-            if (c == '\n')
-                break;
-            sb.append((char) c);
-        }
-        return sb.toString();
-    }
-}
-
-/**
- * Simple blocking listener for eepget.  block in waitForSuccess().
- */
-class SuckerFetchListener implements EepGet.StatusListener {
-    private volatile boolean _complete;
-    private volatile boolean _successful;
-    
-    public SuckerFetchListener() {
-        _complete = false;
-        _successful = false;
-    }
-    
-    public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
-        notifyComplete(true);
-    }
-    
-    public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
-        notifyComplete(false);
-    }
-    
-    private void notifyComplete(boolean ok) {
-        synchronized (this) {
-            _complete = true;
-            _successful = ok;
-            notifyAll();
-        }
-    }
-    
-    /**
-     * Block until the fetch is successful, returning true if it did fetch completely, 
-     * false if it didn't.
-     *
-     */
-    public boolean waitForSuccess() {
-        while (!_complete) {
-            try {
-                synchronized (this) {
-                    wait();
-                }
-            } catch (InterruptedException ie) {}
-        }
-        return _successful;
-    }
-    
-    public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
-        // noop, it may retry
-    }
-    public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
-        // ignore this status update
-    }
-    public void headerReceived(String url, int currentAttempt, String key, String val) {
-        // ignore
-    }
-}
-
-
-
-class SuckerState {
-    String urlToLoad;
-    String outputDir="./sucker_out";
-    String historyPath="./sucker.history";
-    String feedTag="feed";
-    File historyFile;
-    String proxyPort;
-    String proxyHost;
-    String pushScript;
-    int attachmentCounter=0;
-    String messagePath;
-    String baseUrl;
-    boolean importEnclosures=true;
-    boolean importRefs=true;
-    boolean pendingEndLink;
-    boolean shouldProxy;
-    int proxyPortNum;
-    String blog;
-    boolean pushToSyndie;
-    long messageNumber=0;
-    BlogManager bm;
-    User user;
-    List entriesPosted;
-    List history;
-
-    //
-    List fileNames;
-    List fileStreams;
-    List fileTypes;
-    List tempFiles; // deleted after finished push 
-
-    boolean stripNewlines;
-}
\ No newline at end of file
diff --git a/apps/syndie/java/src/net/i2p/syndie/ThreadNodeImpl.java b/apps/syndie/java/src/net/i2p/syndie/ThreadNodeImpl.java
deleted file mode 100644
index 67ede7fb2d94c339219825daef4a23e95991f070..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/ThreadNodeImpl.java
+++ /dev/null
@@ -1,131 +0,0 @@
-package net.i2p.syndie;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.TreeSet;
-
-import net.i2p.data.Hash;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.ThreadNode;
-
-/**
- * Simple memory intensive (but fast) node impl
- *
- */
-class ThreadNodeImpl implements ThreadNode {
-    /** write once, never updated once the tree is created */
-    private Collection _recursiveAuthors;
-    /** contains the BlogURI instances */
-    private Collection _recursiveEntries;
-    /** write once, never updated once the tree is created */
-    private List _children;
-    private BlogURI _entry;
-    private ThreadNode _parent;
-    private BlogURI _parentEntry;
-    private Collection _tags;
-    private Collection _recursiveTags;
-    private long _mostRecentPostDate;
-    private Hash _mostRecentPostAuthor;
-    
-    public ThreadNodeImpl() {
-        _recursiveAuthors = new HashSet(1);
-        _recursiveEntries = new HashSet(1);
-        _children = new ArrayList(1);
-        _entry = null;
-        _parent = null;
-        _parentEntry = null;
-        _tags = new HashSet();
-        _recursiveTags = new HashSet();
-        _mostRecentPostDate = -1;
-        _mostRecentPostAuthor = null;
-    }
-    
-    void setEntry(BlogURI entry) { _entry = entry; }
-    void addAuthor(Hash author) { _recursiveAuthors.add(author); }
-    void addChild(ThreadNodeImpl child) { 
-        if (!_children.contains(child))
-            _children.add(child); 
-    }
-    void setParent(ThreadNodeImpl parent) { _parent = parent; }
-    void setParentEntry(BlogURI parent) { _parentEntry = parent; }
-    void addTag(String tag) { 
-        _tags.add(tag); 
-        _recursiveTags.add(tag);
-    }
-    
-    void summarizeThread() {
-        _recursiveAuthors.add(_entry.getKeyHash());
-        _recursiveEntries.add(_entry);
-        // children are always 'newer' than parents, even if their dates are older
-        // (e.g. post #1 for a child on tuesday is 'newer' than post #5 for the parent on tuesday)
-        _mostRecentPostDate = -1;
-        _mostRecentPostAuthor = null;
-        
-        // we need to go through all children (recursively), in case the 
-        // tree is out of order (which it shouldn't be, if its built carefully...)
-        for (int i = 0; i < _children.size(); i++) {
-            ThreadNodeImpl node = (ThreadNodeImpl)_children.get(i);
-            node.summarizeThread();
-            // >= so we can give reasonable order when a child is a reply to a parent
-            // (since the child must have been posted after the parent)
-            if (node.getMostRecentPostDate() >= _mostRecentPostDate) {
-                _mostRecentPostDate = node.getMostRecentPostDate();
-                _mostRecentPostAuthor = node.getMostRecentPostAuthor();
-            }
-            _recursiveTags.addAll(node.getRecursiveTags());
-            _recursiveAuthors.addAll(node.getRecursiveAuthors());
-            _recursiveEntries.addAll(node.getRecursiveEntries());
-        }
-        
-        if (_mostRecentPostDate < 0) {
-            _mostRecentPostDate = _entry.getEntryId();
-            _mostRecentPostAuthor = _entry.getKeyHash();
-        }
-        
-        // now reorder the children
-        TreeSet ordered = new TreeSet(new NewestNodeFirstComparator());
-        for (int i = 0; i < _children.size(); i++) {
-            ThreadNodeImpl kid = (ThreadNodeImpl)_children.get(i);
-            ordered.add(kid);
-        }
-        List kids = new ArrayList(ordered.size());
-        for (Iterator iter = ordered.iterator(); iter.hasNext(); ) 
-            kids.add(iter.next());
-        _children = kids;
-    }
-    
-    public String toString() {
-        StringBuffer buf = new StringBuffer();
-        buf.append("<node><entry>").append(getEntry().toString()).append("</entry>\n");
-        buf.append("<tags>").append(getTags()).append("</tags>\n");
-        buf.append("<mostRecentPostDate>").append(getMostRecentPostDate()).append("</mostRecentPostDate>\n");
-        buf.append("<recursiveTags>").append(getRecursiveTags()).append("</recursiveTags>\n");
-        buf.append("<children>\n");
-        for (int i = 0; i < _children.size(); i++)
-            buf.append(_children.get(i).toString());
-        buf.append("</children>\n");
-        buf.append("</node>\n");
-        return buf.toString();
-    }
-    
-    private Collection getRecursiveAuthors() { return _recursiveAuthors; }
-    private Collection getRecursiveEntries() { return _recursiveEntries; }
-    
-    // interface-specified methods doing what one would expect...
-    public boolean containsAuthor(Hash author) { return _recursiveAuthors.contains(author); }
-    public boolean containsEntry(BlogURI uri) { return _recursiveEntries.contains(uri); }
-    public ThreadNode getChild(int index) { return (ThreadNode)_children.get(index); }
-    public int getChildCount() { return _children.size(); }
-    public BlogURI getEntry() { return _entry; }
-    public ThreadNode getParent() { return _parent; }
-    public BlogURI getParentEntry() { return _parentEntry; }
-    public boolean containsTag(String tag) { return _tags.contains(tag); }
-    public Collection getTags() { return _tags; }
-    public Collection getRecursiveTags() { return _recursiveTags; }
-    public long getMostRecentPostDate() { return _mostRecentPostDate; }
-    public Hash getMostRecentPostAuthor() { return _mostRecentPostAuthor; }
-    public Iterator getRecursiveAuthorIterator() { return _recursiveAuthors.iterator(); }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/Updater.java b/apps/syndie/java/src/net/i2p/syndie/Updater.java
deleted file mode 100644
index 72a13e60316d427332ce57450326a2ddbebbe426..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/Updater.java
+++ /dev/null
@@ -1,141 +0,0 @@
-package net.i2p.syndie;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-
-import net.i2p.I2PAppContext;
-import net.i2p.client.naming.PetName;
-import net.i2p.client.naming.PetNameDB;
-import net.i2p.syndie.web.RemoteArchiveBean;
-import net.i2p.util.Log;
-
-public class Updater {
-    public static final String VERSION = "1.0";
-    private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(Updater.class);
-    private static final Updater _instance = new Updater();
-    private long _lastUpdate;
-    private static boolean _woken;
-    
-    private static boolean ALLOW_REMOTE_PUSH = false;
-    
-    public void update() {
-        BlogManager bm = BlogManager.instance();
-        if (_lastUpdate + bm.getUpdateDelay()*60*60*1000 > System.currentTimeMillis()) {
-            if (!_woken)
-                return;
-        }
-        _lastUpdate = System.currentTimeMillis();
-        _log.debug("Update started.");
-        String[] archives = bm.getUpdateArchives();
-        for (int i = 0; i < archives.length; i++) {
-            _log.debug("Fetching [" + archives[i] + "]");
-            fetchArchive(archives[i]);
-            _log.debug("Done fetching " + archives[i]);
-        }
-        _log.debug("Done fetching archives");
-        List rssFeeds = bm.getRssFeeds();
-        List allEntries = new ArrayList();
-        Iterator iter = rssFeeds.iterator();
-        while(iter.hasNext()) {
-            String args[] = (String[])iter.next();
-            _log.debug("rss feed begin: " + args[0]);
-            Sucker sucker = new Sucker(args);
-            allEntries.addAll(sucker.suck());
-            _log.debug("rss feed end: " + args[0]);
-        }
-        
-        if (ALLOW_REMOTE_PUSH && (allEntries.size() > 0) ) {
-            String pushedRemoteArchive = getAutomaticallyPushedArchive();
-            if (pushedRemoteArchive != null) {
-                _log.debug("Pushing all of the new entries to " + pushedRemoteArchive + ": " + allEntries);
-                // push all of the new entries to the configured default archive
-                User user = new User();
-                RemoteArchiveBean rab = new RemoteArchiveBean();
-
-                rab.fetchIndex(user, "web", pushedRemoteArchive, bm.getDefaultProxyHost(), bm.getDefaultProxyPort(), true);
-                if (rab.getRemoteIndex() != null) {
-                    rab.postSelectedEntries(user, allEntries, pushedRemoteArchive);
-                    _log.debug(rab.getStatus());
-                } 
-            }
-        }
-        _log.debug("Done with all updating");
-    }
-    
-    /**
-     * Pick the archive to which any posts imported from a feed should be pushed to,
-     * beyond the local archive.  This currently pushes it to the first (alphabetically)
-     * syndie archive in the default user's addressbook that is marked as 'public'.
-     *
-     * @return archive location, or null if no archive should be used
-     */
-    private String getAutomaticallyPushedArchive() {
-        BlogManager bm = BlogManager.instance();
-        User user = bm.getDefaultUser();
-        PetNameDB db = user.getPetNameDB();
-        for (Iterator iter = db.getNames().iterator(); iter.hasNext(); ) {
-            String name = (String)iter.next();
-            PetName pn = db.getByName(name);
-            String proto = pn.getProtocol();
-            if ( (proto != null) && ("syndiearchive".equals(proto)) )
-                if (pn.getIsPublic())
-                    return pn.getLocation();
-        }
-        return null;
-    }
-    
-    public void fetchArchive(String archive) {
-        if ( (archive == null) || (archive.trim().length() <= 0) ) {
-            _log.error("Fetch a null archive?" + new Exception("source"));
-            return;
-        }
-        BlogManager bm = BlogManager.instance();
-        User user = new User();
-        RemoteArchiveBean rab = new RemoteArchiveBean();
-        
-        rab.fetchIndex(user, "web", archive, bm.getDefaultProxyHost(), bm.getDefaultProxyPort(), true);
-        if (rab.getRemoteIndex() != null) {
-            HashMap parameters = new HashMap();
-            parameters.put("action", new String[] {"Fetch all new entries"});
-            rab.fetchSelectedBulk(user, parameters, true);
-        } 
-        _log.debug(rab.getStatus());
-    }
-
-    public static void main() {
-        _woken = false;
-        _instance.run();
-    }
-    
-    public void run() {
-
-        // wait
-        try {
-            Thread.currentThread().sleep(5*60*1000);
-        } catch (InterruptedException ie) {}
-
-        // creates the default user if necessary
-        BlogManager.instance().getDefaultUser();
-        while (true) {
-            int delay = BlogManager.instance().getUpdateDelay();
-            update();
-            try {
-                synchronized (this) {
-                    _woken = false;
-                    wait(delay * 60 * 60 * 1000);
-                }
-            } catch (InterruptedException exp) {
-            }
-
-        }
-    }
- 
-    public static void wakeup() {
-        synchronized (_instance) {
-            _woken = true;
-            _instance.notifyAll();
-        }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/UpdaterServlet.java b/apps/syndie/java/src/net/i2p/syndie/UpdaterServlet.java
deleted file mode 100644
index 8ade5e9cbac5f06cef388540f7ff7129c9dfa30a..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/UpdaterServlet.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package net.i2p.syndie;
-
-import javax.servlet.GenericServlet;
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-
-/**
- * A wrapper for syndie updater to allow it to be started as a web application.
- * 
- * @author Ragnarok
- *
- */
-public class UpdaterServlet extends GenericServlet {
-
-    /* (non-Javadoc)
-     * @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
-     */
-    public void service(ServletRequest request, ServletResponse response) {
-    }
-
-    /* (non-Javadoc)
-     * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
-     */
-    public void init(ServletConfig config) {
-        try {
-            super.init(config);
-        } catch (ServletException exp) {
-        }
-        /*
-        UpdaterThread thread = new UpdaterThread();
-        thread.setDaemon(true);
-        thread.start();
-        System.out.println("INFO: Starting Syndie Updater " + Updater.VERSION);
-         */
-        System.out.println("INFO: Syndie Updater DISABLED.  Use the new Syndie from http://syndie.i2p.net/");
-    }
-
-}
\ No newline at end of file
diff --git a/apps/syndie/java/src/net/i2p/syndie/UpdaterThread.java b/apps/syndie/java/src/net/i2p/syndie/UpdaterThread.java
deleted file mode 100644
index fda8a09a5a173c710401e3bd592cd0c5b5539607..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/UpdaterThread.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package net.i2p.syndie;
-
-/**
- * A thread that runs the updater.  
- * 
- * @author Ragnarok
- *
- */
-public class UpdaterThread extends Thread {
-
-    /**
-     * Construct an UpdaterThread.
-     */
-    public UpdaterThread() {
-    }
-
-    /* (non-Javadoc)
-     * @see java.lang.Runnable#run()
-     */
-    public void run() {
-        //try {
-        //    Thread.sleep(5 * 60 * 1000);
-        //} catch (InterruptedException exp) {
-        //}
-        Updater.main();
-    }
-}
\ No newline at end of file
diff --git a/apps/syndie/java/src/net/i2p/syndie/User.java b/apps/syndie/java/src/net/i2p/syndie/User.java
deleted file mode 100644
index fd386685a526c78da7028b0845d2ddb0ffc240db..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/User.java
+++ /dev/null
@@ -1,350 +0,0 @@
-package net.i2p.syndie;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.StringTokenizer;
-
-import net.i2p.I2PAppContext;
-import net.i2p.client.naming.PetName;
-import net.i2p.client.naming.PetNameDB;
-import net.i2p.data.Base64;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Hash;
-import net.i2p.syndie.web.AddressesServlet;
-
-/**
- * User session state and preferences.
- *
- */
-public class User {
-    private I2PAppContext _context;
-    private String _username;
-    private String _hashedPassword;
-    private Hash _blog;
-    private String _userHash;
-    private long _mostRecentEntry;
-    /** Group name to List of blog selectors, where the selectors are of the form
-     * blog://$key, entry://$key/$entryId, blogtag://$key/$tag, tag://$tag
-     */
-    private Map _blogGroups;
-    /** list of blogs (Hash) we never want to see entries from */
-    private List _shitlistedBlogs;
-    /** where our userhosts.txt is */
-    private String _addressbookLocation;
-    private boolean _showImagesByDefault;
-    private boolean _showExpandedByDefault;
-    private String _defaultSelector;
-    private long _lastLogin;
-    private long _lastMetaEntry;
-    private boolean _allowAccessRemote;
-    private boolean _authenticated;
-    private String _eepProxyHost;
-    private int _eepProxyPort;
-    private String _webProxyHost;
-    private int _webProxyPort;
-    private String _torProxyHost;
-    private int _torProxyPort;
-    private PetNameDB _petnames;
-    private boolean _importAddresses;
-    private boolean _dataImported;
-
-    static final String PROP_USERHASH = "__userHash";
-    
-    private static final String DEFAULT_FAVORITE_TAGS[] = {
-      "syndie", "syndie.tech", "syndie.intro", "syndie.bugs", "syndie.featurerequest", "syndie.announce", 
-      "i2p", "i2p.tech", "i2p.bugs", "i2p.i2phex", "i2p.susimail", "i2p.irc", 
-      "bt.i2psnark", "bt.i2prufus", "bt.i2p-bt", "bt.azureus", "bt.misc",
-      "security.misc",
-      "chat",
-      "test"
-    };
-
-    /**
-     * Ugly hack to fetch the default User instance - this is the default
-     * constructor so it can be used as a bean on the web interface.  If
-     * the Syndie instance isn't in single user mode, the default User 
-     * is an empty unauthenticated User.  If the instance IS in single user
-     * mode, this will contain the logged in 'default' user (creating a new
-     * one as necessary).  If you just want to create a User object, use the
-     * new User(I2PAppContext ctx) constructor.
-     *
-     */
-    public User() {
-        this(I2PAppContext.getGlobalContext());
-        BlogManager.instance().getDefaultUser(this);
-    }
-    
-    public User(I2PAppContext ctx) {
-        _context = ctx;
-        init();
-    }
-    private void init() {
-        _authenticated = false;
-        _username = null;
-        _userHash = null;
-        _hashedPassword = null;
-        _blog = null;
-        _mostRecentEntry = -1;
-        _blogGroups = new HashMap();
-        _shitlistedBlogs = new ArrayList();
-        _defaultSelector = null;
-        _addressbookLocation = "userhosts.txt";
-        _showImagesByDefault = true;
-        _showExpandedByDefault = false;
-        _allowAccessRemote = false;
-        _eepProxyHost = null;
-        _webProxyHost = null;
-        _torProxyHost = null;
-        _eepProxyPort = -1;
-        _webProxyPort = -1;
-        _torProxyPort = -1;
-        _lastLogin = -1;
-        _lastMetaEntry = 0;
-        _petnames = new PetNameDB();
-        _importAddresses = false;
-        _dataImported = false;
-    }
-    
-    public boolean getAuthenticated() { return _authenticated; }
-    public String getUsername() { return _username; }
-    public String getUserHash() { return _userHash; }
-    public Hash getBlog() { return _blog; }
-    public String getBlogStr() { 
-        if (_blog != null)
-            return Base64.encode(_blog.getData()); 
-        else
-            return null;
-    }
-    public long getMostRecentEntry() { return _mostRecentEntry; }
-    public Map getBlogGroups() { return _blogGroups; }
-    public List getShitlistedBlogs() { return _shitlistedBlogs; }
-    public List getFavoriteTags() { 
-        List rv = new ArrayList();
-        for (Iterator iter = _petnames.getNames().iterator(); iter.hasNext(); ) {
-            String name = (String)iter.next();
-            PetName pn = _petnames.getByName(name);
-            if (pn == null) continue;
-            String proto = pn.getProtocol();
-            String loc = pn.getLocation();
-            if ( (proto != null) && (AddressesServlet.PROTO_TAG.equals(proto)) && (loc != null) )
-                rv.add(loc);
-        }
-        if (rv.size() <= 0) {
-            for (int i = 0; i < DEFAULT_FAVORITE_TAGS.length; i++) {
-                if (!_petnames.containsName(DEFAULT_FAVORITE_TAGS[i])) {
-                    _petnames.add(new PetName(DEFAULT_FAVORITE_TAGS[i], AddressesServlet.NET_SYNDIE,
-                                              AddressesServlet.PROTO_TAG, DEFAULT_FAVORITE_TAGS[i]));
-                }
-            }
-        }
-        return rv;
-    }
-    public String getAddressbookLocation() { return _addressbookLocation; }
-    public boolean getShowImages() { return _showImagesByDefault; }
-    public boolean getShowExpanded() { return _showExpandedByDefault; }
-    public long getLastLogin() { return _lastLogin; }
-    public String getHashedPassword() { return _hashedPassword; }
-    public long getLastMetaEntry() { return _lastMetaEntry; }
-    public String getDefaultSelector() { return _defaultSelector; }
-    public void setDefaultSelector(String sel) { _defaultSelector = sel; }
-    public boolean getAllowAccessRemote() { return _allowAccessRemote; }
-    public void setAllowAccessRemote(boolean allow) { _allowAccessRemote = true; }
-    
-    public void setMostRecentEntry(long id) { _mostRecentEntry = id; }
-    public void setLastMetaEntry(long id) { _lastMetaEntry = id; }
-
-    public String getEepProxyHost() { return _eepProxyHost; }
-    public int getEepProxyPort() { return _eepProxyPort; }
-    public String getWebProxyHost() { return _webProxyHost; }
-    public int getWebProxyPort() { return _webProxyPort; }
-    public String getTorProxyHost() { return _torProxyHost; }
-    public int getTorProxyPort() { return _torProxyPort; }
-    
-    public PetNameDB getPetNameDB() { return _petnames; }
-    public boolean getImportAddresses() { return _importAddresses; }
-    
-    public void invalidate() { 
-        if (_authenticated)
-            BlogManager.instance().saveUser(this);
-        init(); 
-    }
-    
-    public void dataImported() { _dataImported = true; }
-    public boolean resetDataImported() { 
-        boolean rv = _dataImported;
-        _dataImported = false;
-        return rv;
-    }
-    
-    public boolean changePassword(String oldPass, String pass0, String pass1) {
-        String curPass = _hashedPassword;
-        Hash curPassHash = _context.sha().calculateHash(DataHelper.getUTF8(oldPass));
-        Hash newPassHash = _context.sha().calculateHash(DataHelper.getUTF8(pass0));
-        if (curPassHash.toBase64().equals(curPass)) {
-            if ( (pass0 != null) && (pass1 != null) && (pass0.equals(pass1)) ) {
-                _hashedPassword = newPassHash.toBase64();
-                return true;
-            } else {
-                return false;
-            }
-        } else {
-            return false;
-        }
-    }
-
-    public String login(String login, String pass, Properties props) {
-        _username = login;
-        load(props);
-        String hpass = Base64.encode(_context.sha().calculateHash(DataHelper.getUTF8(pass)).getData());
-        if (!hpass.equals(_hashedPassword)) {
-            return "<span class=\"b_loginMsgErr\">Incorrect password</span>";
-        }
-        _lastLogin = _context.clock().now();
-        _authenticated = true;
-        return LOGIN_OK;
-    }
-    
-    
-    public void load(Properties props) {
-        _authenticated = false;
-        _hashedPassword = props.getProperty("password");
-        _userHash = props.getProperty(PROP_USERHASH);
-        
-        // blog=luS9d3uaf....HwAE=
-        String b = props.getProperty("blog");
-        if (b != null) _blog = new Hash(Base64.decode(b));
-        // lastid=12345
-        String id = props.getProperty("lastid");
-        if (id != null) try { _mostRecentEntry = Long.parseLong(id); } catch (NumberFormatException nfe) {}
-        // lastmetaedition=12345
-        id = props.getProperty("lastmetaedition");
-        if (id != null) try { _lastMetaEntry = Long.parseLong(id); } catch (NumberFormatException nfe) {}
-        // groups=abc:selector,selector,selector,selector def:selector,selector,selector
-        StringTokenizer tok = new StringTokenizer(props.getProperty("groups", ""), " ");
-        while (tok.hasMoreTokens()) {
-            String group = tok.nextToken();
-            int endName = group.indexOf(':');
-            if (endName <= 0)
-                continue;
-            String groupName = group.substring(0, endName);
-            String sel = group.substring(endName+1);
-            List selectors = new ArrayList();
-            while ( (sel != null) && (sel.length() > 0) ) {
-                int end = sel.indexOf(',');
-                if (end < 0) {
-                    selectors.add(sel);
-                    sel = null;
-                } else {
-                    if (end + 1 >= sel.length()) {
-                        selectors.add(sel.substring(0,end));
-                        sel = null;
-                    } else if (end == 0) {
-                        sel = sel.substring(1);
-                    } else {
-                        selectors.add(sel.substring(0, end));
-                        sel = sel.substring(end+1);
-                    }
-                }
-            }
-            _blogGroups.put(groupName.trim(), selectors);
-        }
-        // shitlist=hash,hash,hash
-        tok = new StringTokenizer(props.getProperty("shitlistedblogs", ""), ",");
-        while (tok.hasMoreTokens()) {
-            String blog = tok.nextToken();
-            byte bl[] = Base64.decode(blog);
-            if ( (bl != null) && (bl.length == Hash.HASH_LENGTH) )
-                _shitlistedBlogs.add(new Hash(bl));
-        }
-        
-        String addr = props.getProperty("addressbook", "userhosts.txt");
-        if (addr != null) {
-            _addressbookLocation = addr;
-            try {
-                _petnames.load(addr);
-            } catch (IOException ioe) {
-                ioe.printStackTrace();
-            }
-        }
-        
-        String show = props.getProperty("showimages", "true");
-        _showImagesByDefault = (show != null) && (show.equals("true"));
-        show = props.getProperty("showexpanded", "false");
-        _showExpandedByDefault = (show != null) && (show.equals("true"));
-        _defaultSelector = props.getProperty("defaultselector");
-        String allow = props.getProperty("allowaccessremote", "false");
-        _allowAccessRemote = (allow != null) && (allow.equals("true"));
-        _eepProxyPort = getInt(props.getProperty("eepproxyport"));
-        _webProxyPort = getInt(props.getProperty("webproxyport"));
-        _torProxyPort = getInt(props.getProperty("torproxyport"));
-        _eepProxyHost = props.getProperty("eepproxyhost");
-        _webProxyHost = props.getProperty("webproxyhost");
-        _torProxyHost = props.getProperty("torproxyhost");
-        String importadr = props.getProperty("importaddresses", "false");
-        _importAddresses = (importadr != null) && (importadr.equals("true"));
-    }
-    
-    private int getInt(String val) {
-        if (val == null) return -1;
-        try { return Integer.parseInt(val); } catch (NumberFormatException nfe) { return -1; }
-    }
-    
-    public static final String LOGIN_OK = "<span class=\"b_loginMsgOk\">Logged in</span>";
-    
-    public String export() {
-        StringBuffer buf = new StringBuffer(512);
-        buf.append("password=" + getHashedPassword() + "\n");
-        buf.append("blog=" + getBlog().toBase64() + "\n");
-        buf.append("lastid=" + getMostRecentEntry() + "\n");
-        buf.append("lastmetaedition=" + getLastMetaEntry() + "\n");
-        buf.append("lastlogin=" + getLastLogin() + "\n");
-        buf.append("addressbook=" + getAddressbookLocation() + "\n");
-        buf.append("showimages=" + getShowImages() + "\n");
-        buf.append("showexpanded=" + getShowExpanded() + "\n");
-        buf.append("defaultselector=" + getDefaultSelector() + "\n");
-        buf.append("allowaccessremote=" + _allowAccessRemote + "\n");
-        buf.append("importaddresses=" + getImportAddresses() + "\n");
-        buf.append("groups=");
-        Map groups = getBlogGroups();
-        for (Iterator iter = groups.keySet().iterator(); iter.hasNext(); ) {
-            String name = (String)iter.next();
-            List selectors = (List)groups.get(name);
-            buf.append(name).append(':');
-            for (int i = 0; i < selectors.size(); i++) {
-                buf.append(selectors.get(i));
-                if (i + 1 < selectors.size())
-                    buf.append(",");
-            }
-            if (iter.hasNext())
-                buf.append(' ');
-        }
-        buf.append('\n');
-        // shitlist=hash,hash,hash
-        List shitlistedBlogs = getShitlistedBlogs();
-        if (shitlistedBlogs.size() > 0) {
-            //buf.setLength(0);
-            buf.append("shitlistedblogs=");
-            for (int i = 0; i < shitlistedBlogs.size(); i++) {
-                Hash blog = (Hash)shitlistedBlogs.get(i);
-                buf.append(blog.toBase64());
-                if (i + 1 < shitlistedBlogs.size())
-                    buf.append(',');
-            }
-            buf.append('\n');
-        }
-        List favoriteTags = getFavoriteTags();
-        if (favoriteTags.size() > 0) {
-            buf.append("favoritetags=");
-            for (int i = 0; i < favoriteTags.size(); i++)
-                buf.append(((String)favoriteTags.get(i)).trim()).append(" ");
-            buf.append('\n');
-        }
-
-        return buf.toString();
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/Version.java b/apps/syndie/java/src/net/i2p/syndie/Version.java
deleted file mode 100644
index 4500e5d566ca1562f4434db5bbe19fcbdbf57161..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/Version.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package net.i2p.syndie;
-
-/**
- *
- */
-public class Version {
-    public static final String VERSION = "0-alpha";
-    public static final String BUILD = "0";
-    public static final String INDEX_VERSION = "1.0";
-    public static final String ID = "$Id$";
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/WritableThreadIndex.java b/apps/syndie/java/src/net/i2p/syndie/WritableThreadIndex.java
deleted file mode 100644
index 996148bdfdbdeec9664275248b5962b660045cff..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/WritableThreadIndex.java
+++ /dev/null
@@ -1,145 +0,0 @@
-package net.i2p.syndie;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.TreeSet;
-
-import net.i2p.I2PAppContext;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.syndie.data.ThreadIndex;
-import net.i2p.syndie.data.ThreadNode;
-import net.i2p.syndie.sml.HTMLRenderer;
-import net.i2p.syndie.sml.SMLParser;
-
-/**
- *
- */
-class WritableThreadIndex extends ThreadIndex {
-    /** map of child (BlogURI) to parent (BlogURI) */
-    private Map _parents;
-    /** map of entry (BlogURI) to tags (String[]) */
-    private Map _tags;
-    private static final String[] NO_TAGS = new String[0];
-    /** b0rk if the thread seems to go too deep */
-    private static final int MAX_THREAD_DEPTH = 64;
-    
-    WritableThreadIndex() {
-        super(); 
-        _parents = new HashMap();
-        _tags = new TreeMap(new NewestEntryFirstComparator());
-    }
-    
-    void addParent(BlogURI parent, BlogURI child) { _parents.put(child, parent); }
-    void addEntry(BlogURI entry, String tags[]) { 
-        if (tags == null) tags = NO_TAGS;
-        Object old = _tags.get(entry);
-        if (old != null) {
-            System.err.println("Old value: " + old + " new tags: " + tags + " entry: " + entry);
-        } else {
-            _tags.put(entry, tags);
-        }
-    }
-    
-    /** 
-     * pull the data added together into threads, and stash them in the 
-     * roots, organized chronologically
-     *
-     */
-    void organizeTree() {
-        Map nodes = new HashMap(_tags.size());
-        for (Iterator iter = _tags.keySet().iterator(); iter.hasNext(); ) {
-            BlogURI entry = (BlogURI)iter.next();
-            String tags[] = (String[])_tags.get(entry);
-            BlogURI parent = (BlogURI)_parents.get(entry);
-            ThreadNodeImpl node = new ThreadNodeImpl();
-            node.setEntry(entry);
-            if (tags != null)
-                for (int i = 0; i < tags.length; i++)
-                    node.addTag(tags[i]);
-            if (parent != null)
-                node.setParentEntry(parent);
-            addEntry(entry, node);
-            nodes.put(entry, node);
-        }
-        
-        SMLParser parser = new SMLParser(I2PAppContext.getGlobalContext());
-        HeaderReceiver rec = new HeaderReceiver();
-        Archive archive = BlogManager.instance().getArchive();
-        
-        for (Iterator iter = nodes.keySet().iterator(); iter.hasNext(); ) {
-            BlogURI entry = (BlogURI)iter.next();
-            ThreadNodeImpl node = (ThreadNodeImpl)nodes.get(entry);
-            int depth = 0;
-            // climb the tree
-            while (node.getParentEntry() != null) {
-                ThreadNodeImpl parent = (ThreadNodeImpl)nodes.get(node.getParentEntry());
-                if (parent == null) break;
-                
-                // if the parent doesn't want replies, only include replies under the tree
-                // if they're written by the same author
-                BlogURI parentURI = parent.getEntry();
-                EntryContainer parentEntry = archive.getEntry(parentURI);
-                if (parentEntry != null) {
-                    parser.parse(parentEntry.getEntry().getText(), rec);
-                    String refuse = rec.getHeader(HTMLRenderer.HEADER_REFUSE_REPLIES);
-                    if ( (refuse != null) && (Boolean.valueOf(refuse).booleanValue()) ) {
-                        if (parent.getEntry().getKeyHash().equals(entry.getKeyHash())) {
-                            // same author, allow the reply
-                        } else {
-                            // different author, refuse
-                            parent = null;
-                            break;
-                        }
-                    }
-                }
-                
-                node.setParent(parent);
-                parent.addChild(node);
-                node = parent;
-                depth++;
-                if (depth > MAX_THREAD_DEPTH)
-                    break;
-            }
-        
-            node.summarizeThread();
-        }
-        
-        // we do this in a second pass, since we need the data built by the
-        // summarizeThread() of a fully constructed tree
-        
-        TreeSet roots = new TreeSet(new NewestNodeFirstComparator());
-        for (Iterator iter = nodes.keySet().iterator(); iter.hasNext(); ) {
-            BlogURI entry = (BlogURI)iter.next();
-            ThreadNode node = (ThreadNode)nodes.get(entry);
-            int depth = 0;
-            // climb the tree
-            while (node.getParent() != null)
-                node = node.getParent();
-        
-            if (!roots.contains(node)) {
-                roots.add(node);
-            }
-        }
-        
-        // store them, sorted by most recently updated thread first
-        for (Iterator iter = roots.iterator(); iter.hasNext(); ) 
-            addRoot((ThreadNode)iter.next());
-        
-        _parents.clear();
-        _tags.clear();
-    }
-    
-    public String toString() {
-        StringBuffer buf = new StringBuffer();
-        buf.append("<threadIndex>");
-        for (int i = 0; i < getRootCount(); i++) {
-            ThreadNode root = getRoot(i);
-            buf.append(root.toString());
-        }
-        buf.append("</threadIndex>\n");
-        return buf.toString();
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/data/ArchiveIndex.java b/apps/syndie/java/src/net/i2p/syndie/data/ArchiveIndex.java
deleted file mode 100644
index 4e92ce2c8d6a63336ecec5b34a10b84c96559b54..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/data/ArchiveIndex.java
+++ /dev/null
@@ -1,510 +0,0 @@
-package net.i2p.syndie.data;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.TreeMap;
-
-import net.i2p.I2PAppContext;
-import net.i2p.data.Base64;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.util.Log;
-
-/**
- * Simple read-only summary of an archive
- */
-public class ArchiveIndex {
-    private I2PAppContext _context;
-    private Log _log;
-    protected String _version;
-    protected long _generatedOn;
-    protected int _allBlogs;
-    protected int _newBlogs;
-    protected int _allEntries;
-    protected int _newEntries;
-    protected long _totalSize;
-    protected long _newSize;
-    /** list of BlogSummary objects */
-    protected List _blogs;
-    /** list of Hash objects */
-    protected List _newestBlogs;
-    /** list of BlogURI objects */
-    protected List _newestEntries;
-    /** parent message to a set of replies, ordered with the oldest first */
-    protected Map _replies;
-    protected Properties _headers;
-    private ThreadIndex _threadedIndex;
-    
-    public ArchiveIndex() {
-        this(I2PAppContext.getGlobalContext(), false);
-    }
-    public ArchiveIndex(I2PAppContext ctx) {
-        this(ctx, false); //true);
-    }
-    public ArchiveIndex(I2PAppContext ctx, boolean shouldLoad) {
-        _context = ctx;
-        _log = ctx.logManager().getLog(ArchiveIndex.class);
-        _blogs = new ArrayList();
-        _newestBlogs = new ArrayList();
-        _newestEntries = new ArrayList();
-        _headers = new Properties();
-        _replies = Collections.synchronizedMap(new HashMap());
-        _generatedOn = -1;
-        _threadedIndex = null;
-        if (shouldLoad)
-            setIsLocal("true");
-    }
-    
-    public String getVersion() { return _version; }
-    public Properties getHeaders() { return _headers; }
-    public int getAllBlogs() { return _allBlogs; }
-    public int getNewBlogs() { return _newBlogs; }
-    public int getAllEntries() { return _allEntries; }
-    public int getNewEntries() { return _newEntries; }
-    public long getTotalSize() { return _totalSize; }
-    public long getNewSize() { return _newSize; }
-    public long getGeneratedOn() { return _generatedOn; }
-    public ThreadIndex getThreadedIndex() { return _threadedIndex; }
-    public void setThreadedIndex(ThreadIndex index) { _threadedIndex = index; }
-    
-    public String getNewSizeStr() { 
-        if (_newSize < 1024) return _newSize + "";
-        if (_newSize < 1024*1024) return _newSize/1024 + "KB";
-        else return _newSize/(1024*1024) + "MB";
-    }
-    public String getTotalSizeStr() { 
-        if (_totalSize < 1024) return _totalSize + "";
-        if (_totalSize < 1024*1024) return _totalSize/1024 + "KB";
-        else return _totalSize/(1024*1024) + "MB";
-    }
-    
-    /** how many blogs/tags are indexed */
-    public int getIndexBlogs() { return _blogs.size(); }
-    /** get the blog used for the given blog/tag pair */
-    public Hash getBlog(int index) { return ((BlogSummary)_blogs.get(index)).blog; }
-    /** get the tag used for the given blog/tag pair */
-    public String getBlogTag(int index) { return ((BlogSummary)_blogs.get(index)).tag; }
-    /** get the highest entry ID for the given blog/tag pair */
-    public long getBlogLastUpdated(int index) { return ((BlogSummary)_blogs.get(index)).lastUpdated; }
-    /** get the entry count for the given blog/tag pair */
-    public int getBlogEntryCount(int index) { return ((BlogSummary)_blogs.get(index)).entries.size(); }
-    /** get the entry from the given blog/tag pair */
-    public BlogURI getBlogEntry(int index, int entryIndex) { return ((EntrySummary)((BlogSummary)_blogs.get(index)).entries.get(entryIndex)).entry; }
-    /** get the raw entry size (including attachments) from the given blog/tag pair */
-    public long getBlogEntrySizeKB(int index, int entryIndex) { return ((EntrySummary)((BlogSummary)_blogs.get(index)).entries.get(entryIndex)).size; }
-    
-    public boolean getEntryIsKnown(BlogURI uri) { return getEntry(uri) != null; }
-    public long getBlogEntrySizeKB(BlogURI uri) {
-        EntrySummary entry = getEntry(uri);
-        if (entry == null) return -1;
-        return entry.size;
-    }
-    private EntrySummary getEntry(BlogURI uri) {
-        if ( (uri == null) || (uri.getKeyHash() == null) || (uri.getEntryId() < 0) ) return null;
-        for (int i = 0; i < _blogs.size(); i++) {
-            BlogSummary summary = (BlogSummary)_blogs.get(i);
-            if (summary.blog.equals(uri.getKeyHash())) {
-                for (int j = 0; j < summary.entries.size(); j++) {
-                    EntrySummary entry = (EntrySummary)summary.entries.get(j);
-                    if (entry.entry.equals(uri))
-                        return entry;
-                }
-            }
-        }
-        return null;
-    }
-    public Set getBlogEntryTags(BlogURI uri) {
-        Set tags = new HashSet();
-        if ( (uri == null) || (uri.getKeyHash() == null) || (uri.getEntryId() < 0) ) return tags;
-        for (int i = 0; i < _blogs.size(); i++) {
-            BlogSummary summary = (BlogSummary)_blogs.get(i);
-            if (summary.blog.equals(uri.getKeyHash())) {
-                for (int j = 0; j < summary.entries.size(); j++) {
-                    EntrySummary entry = (EntrySummary)summary.entries.get(j);
-                    if (entry.entry.equals(uri)) {
-                        tags.add(summary.tag);
-                        break;
-                    }
-                }
-            }
-        }
-        return tags;
-    }
-    public int getBlogEntryCount(Hash blog) {
-        Set uris = new HashSet(64);
-        for (int i = 0; i < _blogs.size(); i++) {
-            BlogSummary summary = (BlogSummary)_blogs.get(i);
-            if (summary.blog.equals(blog)) {
-                uris.addAll(summary.entries);
-                //for (int j = 0; j < summary.entries.size(); j++) {
-                //    EntrySummary entry = (EntrySummary)summary.entries.get(j);
-                //    uris.add(entry.entry);
-                //}
-            }
-        }
-        return uris.size();
-    }
-    
-    /** how many 'new' blogs are listed */
-    public int getNewestBlogCount() { return _newestBlogs.size(); }
-    public Hash getNewestBlog(int index) { return (Hash)_newestBlogs.get(index); }
-    /** how many 'new' entries are listed */
-    public int getNewestBlogEntryCount() { return _newestEntries.size(); }
-    public BlogURI getNewestBlogEntry(int index) { return (BlogURI)_newestEntries.get(index); }
-    
-    /** list of locally known tags (String) under the given blog */
-    public List getBlogTags(Hash blog) {
-        List rv = new ArrayList();
-        for (int i = 0; i < _blogs.size(); i++) {
-            if (getBlog(i).equals(blog))
-                rv.add(getBlogTag(i));
-        }
-        return rv;
-    }
-    /** list of unique blogs locally known (set of Hash) */
-    public Set getUniqueBlogs() {
-        Set rv = new HashSet();
-        for (int i = 0; i < _blogs.size(); i++) {
-            rv.add(getBlog(i));
-        }
-        return rv;
-    }
-    public List getReplies(BlogURI uri) {
-        Set replies = (Set)_replies.get(uri);
-        if (replies == null) return Collections.EMPTY_LIST;
-        synchronized (replies) {
-            return new ArrayList(replies);
-        }
-    }
-    public void setLocation(String location) {
-        try {
-            File l = new File(location);
-            if (l.exists())
-                load(l);
-        } catch (IOException ioe) {
-            ioe.printStackTrace();
-        }
-    }
-    public void setIsLocal(String val) {
-        if ("true".equals(val)) {
-            try {
-                File dir = BlogManager.instance().getArchive().getArchiveDir();
-                load(new File(dir, Archive.INDEX_FILE));
-            } catch (IOException ioe) {}
-        }
-    }
-    
-    public void load(File location) throws IOException {
-        FileInputStream in = null;
-        try {
-            in = new FileInputStream(location);
-            load(in);
-        } finally {
-            if (in != null)
-                try { in.close(); } catch (IOException ioe) {}
-        }
-    }
-    
-    /** load up the index from an archive.txt */
-    public void load(InputStream index) throws IOException {
-        _allBlogs = 0;
-        _allEntries = 0;
-        _newBlogs = 0;
-        _newEntries = 0;
-        _newSize = 0;
-        _totalSize = 0;
-        _version = null;
-        _blogs = new ArrayList();
-        _newestBlogs = new ArrayList();
-        _newestEntries = new ArrayList();
-        _headers = new Properties();
-        BufferedReader in = new BufferedReader(new InputStreamReader(index, "UTF-8"));
-        String line = null;
-        line = in.readLine();
-        if (line == null)
-            return;
-        if (!line.startsWith("SyndieVersion:"))
-            throw new IOException("Index is invalid - it starts with " + line);
-        _version = line.substring("SyndieVersion:".length()).trim();
-        if (!_version.startsWith("1."))
-            throw new IOException("Index is not supported, we only handle versions 1.*, but it is " + _version);
-        while ( (line = in.readLine()) != null) {
-            if (line.length() <= 0)
-                break;
-            if (line.startsWith("Blog:")) break;
-            int split = line.indexOf(':');
-            if (split <= 0) continue;
-            if (split >= line.length()-1) continue;
-            _headers.setProperty(line.substring(0, split), line.substring(split+1));
-        }
-        if (line != null) {
-            do {
-                if (!line.startsWith("Blog:"))
-                    break;
-                loadBlog(line);
-            } while ( (line = in.readLine()) != null);
-        }
-        
-        // ignore the first line that doesnt start with blog - its blank
-        while ( (line = in.readLine()) != null) {
-            int split = line.indexOf(':');
-            if (split <= 0) continue;
-            if (split >= line.length()-1) continue;
-            String key = line.substring(0, split);
-            String val = line.substring(split+1);
-            if (key.equals("AllBlogs"))
-                _allBlogs = getInt(val);
-            else if (key.equals("NewBlogs"))
-                _newBlogs = getInt(val);
-            else if (key.equals("AllEntries"))
-                _allEntries = getInt(val);
-            else if (key.equals("NewEntries"))
-                _newEntries = getInt(val);
-            else if (key.equals("TotalSize"))
-                _totalSize = getInt(val);
-            else if (key.equals("NewSize"))
-                _newSize = getInt(val);
-            else if (key.equals("NewestBlogs"))
-                _newestBlogs = parseNewestBlogs(val);
-            else if (key.equals("NewestEntries"))
-                _newestEntries = parseNewestEntries(val);
-            //else
-            //    System.err.println("Key: " + key + " val: " + val);
-        }
-    }
-    
-    /**
-     * Dig through the index for BlogURIs matching the given criteria, ordering the results by
-     * their own entryIds.  
-     *
-     * @param out where to store the matches
-     * @param blog if set, what blog key must the entries be under
-     * @param tag if set, what tag must the entry be in
-     *
-     */
-    public void selectMatchesOrderByEntryId(List out, Hash blog, String tag) {
-        selectMatchesOrderByEntryId(out, blog, tag, 0);
-    }
-    public void selectMatchesOrderByEntryId(List out, Hash blog, String tag, long lowestEntryId) {
-        TreeMap ordered = new TreeMap();
-        for (int i = 0; i < _blogs.size(); i++) {
-            BlogSummary summary = (BlogSummary)_blogs.get(i);
-            if (blog != null) {
-                if (!blog.equals(summary.blog))
-                    continue;
-            }
-            
-            if ( (tag != null) && (tag.trim().length() > 0) ) {
-                if (!tag.equals(summary.tag)) {
-                    if (_log.shouldLog(Log.DEBUG))
-                        _log.debug("Tag [" + summary.tag + "] does not match the requested [" + tag + "] in " + summary.blog.toBase64());
-                    if (false) {
-                        StringBuffer b = new StringBuffer(tag.length()*2);
-                        for (int j = 0; j < tag.length(); j++) {
-                            b.append((int)tag.charAt(j));
-                            b.append('.');
-                            if (summary.tag.length() > j+1)
-                                b.append((int)summary.tag.charAt(j));
-                            else
-                                b.append('_');
-                            b.append(' ');
-                        }
-                        if (_log.shouldLog(Log.DEBUG))
-                            _log.debug("tag.summary: " + b.toString());
-                    }
-                    continue;
-                }
-            }
-            
-            for (int j = 0; j < summary.entries.size(); j++) {
-                EntrySummary entry = (EntrySummary)summary.entries.get(j);
-                if (entry.entry.getEntryId() < lowestEntryId) {
-                    long daysAgo1 = entry.entry.getEntryId() / (24*60*60*1000l);
-                    long daysAgo2 = lowestEntryId / (24*60*60*1000l);
-                    continue;
-                } else {                    
-                  String k = (Long.MAX_VALUE-entry.entry.getEntryId()) + "-" + entry.entry.getKeyHash().toBase64();
-                  ordered.put(k, entry.entry);
-                }
-            }
-        }
-        for (Iterator iter = ordered.values().iterator(); iter.hasNext(); ) {
-            BlogURI entry = (BlogURI)iter.next();
-            if (entry.getEntryId() < lowestEntryId) {
-                continue;
-            }
-            if (!out.contains(entry))
-                out.add(entry);
-        }
-    }
-    
-    private static final int getInt(String val) {
-        try {
-            return Integer.parseInt(val.trim()); 
-        } catch (NumberFormatException nfe) {
-            nfe.printStackTrace();
-            return 0;
-        }
-    }
-    
-    private List parseNewestBlogs(String vals) {
-        List rv = new ArrayList();
-        StringTokenizer tok = new StringTokenizer(vals, " \t\n");
-        while (tok.hasMoreTokens())
-            rv.add(new Hash(Base64.decode(tok.nextToken())));
-        return rv;
-    }
-    private List parseNewestEntries(String vals) {
-        List rv = new ArrayList();
-        StringTokenizer tok = new StringTokenizer(vals, " \t\n");
-        while (tok.hasMoreTokens())
-            rv.add(new BlogURI(tok.nextToken()));
-        return rv;
-    }
-    
-    private void loadBlog(String line) throws IOException {
-        // Blog: hash YYYYMMDD tag\t[ yyyymmdd_n_sizeKB]*
-        StringTokenizer tok = new StringTokenizer(line.trim(), " \n\t");
-        if (tok.countTokens() < 4)
-            return;
-        tok.nextToken();
-        String keyStr = tok.nextToken();
-        byte k[] = Base64.decode(keyStr);
-        if ( (k == null) || (k.length != Hash.HASH_LENGTH) )
-            return; // ignore bad hashes
-        Hash keyHash = new Hash(k);
-        String whenStr = tok.nextToken();
-        long when = getIndexDate(whenStr);
-        String tag = tok.nextToken();
-        BlogSummary summary = new BlogSummary();
-        summary.blog = keyHash;
-        summary.tag = tag.trim();
-        summary.lastUpdated = when;
-        summary.entries = new ArrayList();
-        while (tok.hasMoreTokens()) {
-            String entry = tok.nextToken();
-            long id = Archive.getEntryIdFromIndexName(entry);
-            int kb = Archive.getSizeFromIndexName(entry);
-            summary.entries.add(new EntrySummary(new BlogURI(keyHash, id), kb));
-        }
-        _blogs.add(summary);
-    }
-    
-    private SimpleDateFormat _dateFmt = new SimpleDateFormat("yyyyMMdd", Locale.UK);
-    private long getIndexDate(String yyyymmdd) {
-        synchronized (_dateFmt) {
-            try {
-                return _dateFmt.parse(yyyymmdd).getTime();
-            } catch (ParseException pe) {
-                return -1;
-            }
-        } 
-    }
-    private String getIndexDate(long when) {
-        synchronized (_dateFmt) {
-            return _dateFmt.format(new Date(when));
-        }
-    }
-    
-    protected class BlogSummary {
-        Hash blog;
-        String tag;
-        long lastUpdated;
-        /** list of EntrySummary objects */
-        List entries;
-        
-        public BlogSummary() {
-            entries = new ArrayList();
-        }
-    }
-    protected class EntrySummary {
-        BlogURI entry;
-        long size;
-        public EntrySummary(BlogURI uri, long kb) {
-            size = kb;
-            entry = uri;
-        }
-        public int hashCode() {
-            return entry.hashCode();
-        }
-        public boolean equals(Object obj) {
-            if ( (obj instanceof EntrySummary) && (((EntrySummary)obj).entry.equals(entry)) )
-                return true;
-            return false;
-        }
-    }
-    
-    /** export the index into an archive.txt */
-    public String toString() {
-        StringBuffer rv = new StringBuffer(1024);
-        rv.append("SyndieVersion: ").append(_version).append('\n');
-        for (Iterator iter = _headers.keySet().iterator(); iter.hasNext(); ) {
-            String key = (String)iter.next();
-            String val = _headers.getProperty(key);
-            rv.append(key).append(": ").append(val).append('\n');
-        }
-        for (int i = 0; i < _blogs.size(); i++) {
-            rv.append("Blog: ");
-            Hash blog = getBlog(i);
-            String tag = getBlogTag(i);
-            rv.append(Base64.encode(blog.getData())).append(' ');
-            rv.append(getIndexDate(getBlogLastUpdated(i))).append(' ');
-            rv.append(tag).append('\t');
-            int entries = getBlogEntryCount(i);
-            for (int j = 0; j < entries; j++) {
-                BlogURI entry = getBlogEntry(i, j);
-                long kb = getBlogEntrySizeKB(i, j);
-                rv.append(Archive.getIndexName(entry.getEntryId(), (int)kb*1024)).append(' ');
-            }
-            rv.append('\n');
-        }
-        
-        rv.append('\n');
-        rv.append("AllBlogs: ").append(_allBlogs).append('\n');
-        rv.append("NewBlogs: ").append(_newBlogs).append('\n');
-        rv.append("AllEntries: ").append(_allEntries).append('\n');
-        rv.append("NewEntries: ").append(_newEntries).append('\n');
-        rv.append("TotalSize: ").append(_totalSize).append('\n');
-        rv.append("NewSize: ").append(_newSize).append('\n');
-        
-        rv.append("NewestBlogs: ");
-        for (int i = 0; i < _newestBlogs.size(); i++)
-            rv.append(((Hash)(_newestBlogs.get(i))).toBase64()).append(' ');
-        rv.append('\n');
-            
-        rv.append("NewestEntries: ");
-        for (int i = 0; i < _newestEntries.size(); i++)
-            rv.append(((BlogURI)_newestEntries.get(i)).toString()).append(' ');
-        rv.append('\n');
-        return rv.toString();
-    }
-    
-    
-    /** Usage: ArchiveIndex archive.txt */
-    public static void main(String args[]) {
-        try {
-            ArchiveIndex i = new ArchiveIndex();
-            i.load(new File(args[0]));
-            System.out.println(i.toString());
-        } catch (IOException ioe) { ioe.printStackTrace(); }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/data/Attachment.java b/apps/syndie/java/src/net/i2p/syndie/data/Attachment.java
deleted file mode 100644
index f3efa2396d157ab6e47f32714445d238b598ccad..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/data/Attachment.java
+++ /dev/null
@@ -1,128 +0,0 @@
-package net.i2p.syndie.data;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import net.i2p.data.DataHelper;
-
-/**
- *
- */
-public class Attachment {
-    private byte _data[];
-    private byte _rawMetadata[];
-    private List _keys;
-    private List _values;
-    
-    public Attachment(byte data[], byte metadata[]) {
-        _data = data;
-        _rawMetadata = metadata;
-        _keys = new ArrayList();
-        _values = new ArrayList();
-        parseMeta();
-    }
-    
-    public static final String NAME = "Name";
-    public static final String DESCRIPTION = "Description";
-    public static final String MIMETYPE = "MimeType";
-    
-    public Attachment(byte data[], String name, String description, String mimeType) {
-        _data = data;
-        _keys = new ArrayList();
-        _values = new ArrayList();
-        _keys.add(NAME);
-        _values.add(name);
-        if ( (description != null) && (description.trim().length() > 0) ) {
-            _keys.add(DESCRIPTION);
-            _values.add(description);
-        }
-        if ( (mimeType != null) && (mimeType.trim().length() > 0) ) {
-            _keys.add(MIMETYPE);
-            _values.add(mimeType);
-        }
-        createMeta();
-    }
-    
-    public byte[] getData() { return _data; }
-    public int getDataLength() { return _data.length; }
-    public byte[] getRawMetadata() { return _rawMetadata; }
-    
-    public InputStream getDataStream() throws IOException { return new ByteArrayInputStream(_data); }
-    
-    public String getMeta(String key) { 
-        for (int i = 0; i < _keys.size(); i++) {
-            if (key.equals(_keys.get(i)))
-                return (String)_values.get(i);
-        }
-        return null;
-    }
-    
-    public String getName() { return getMeta(NAME); }
-    public String getDescription() { return getMeta(DESCRIPTION); }
-    public String getMimeType() { return getMeta(MIMETYPE); }
-    
-    public void setMeta(String key, String val) {
-        for (int i = 0; i < _keys.size(); i++) {
-            if (key.equals(_keys.get(i))) {
-                _values.set(i, val);
-                return;
-            }
-        }
-        _keys.add(key);
-        _values.add(val);
-    }
-    
-    public Map getMeta() {
-        Map rv = new HashMap(_keys.size());
-        for (int i = 0; i < _keys.size(); i++) {
-            String k = (String)_keys.get(i);
-            String v = (String)_values.get(i);
-            rv.put(k,v);
-        }
-        return rv;
-    }
-    
-    private void createMeta() {
-        StringBuffer meta = new StringBuffer(64);
-        for (int i = 0; i < _keys.size(); i++) {
-            meta.append(_keys.get(i)).append(':').append(_values.get(i)).append('\n');
-        }
-        _rawMetadata = DataHelper.getUTF8(meta);
-    }
-    
-    private void parseMeta() {
-        if (_rawMetadata == null) return;
-        String key = null;
-        String val = null;
-        int keyBegin = 0;
-        int valBegin = -1;
-        for (int i = 0; i < _rawMetadata.length; i++) {
-            if (_rawMetadata[i] == ':') {
-                key = DataHelper.getUTF8(_rawMetadata, keyBegin, i - keyBegin);
-                valBegin = i + 1;
-            } else if (_rawMetadata[i] == '\n') {
-                val = DataHelper.getUTF8(_rawMetadata, valBegin, i - valBegin);
-                _keys.add(key);
-                _values.add(val);
-                keyBegin = i + 1;
-                key = null;
-                val = null;
-            }
-        }
-    }
-    
-    public String toString() { 
-        int len = 0;
-        if (_data != null)
-            len = _data.length;
-        return getName() 
-               + (getDescription() != null ? ": " + getDescription() : "") 
-               + (getMimeType() != null ? ", type: " + getMimeType() : "") 
-               + ", size: " + len; 
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/data/BlogInfo.java b/apps/syndie/java/src/net/i2p/syndie/data/BlogInfo.java
deleted file mode 100644
index 82613096c7064667b1ca75ef905ffb8018bb18f1..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/data/BlogInfo.java
+++ /dev/null
@@ -1,299 +0,0 @@
-package net.i2p.syndie.data;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Properties;
-import java.util.StringTokenizer;
-
-import net.i2p.I2PAppContext;
-import net.i2p.data.Base64;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Signature;
-import net.i2p.data.SigningPrivateKey;
-import net.i2p.data.SigningPublicKey;
-import net.i2p.util.Log;
-
-/**
- * Blog metadata.  Formatted as: <pre>
- * [key:val\n]*
- * </pre>
- *
- * Required keys:
- *  Owner: base64 of their signing public key
- *  Signature: base64 of the DSA signature of the rest of the ordered metadata
- *  Edition: base10 unique identifier for this metadata (higher clobbers lower)
- *
- * Optional keys:
- *  Posters: comma delimited list of base64 signing public keys that
- *           can post to the blog
- *  Name: name of the blog
- *  Description: brief description of the blog
- *  
- */
-public class BlogInfo {
-    private SigningPublicKey _key;
-    private SigningPublicKey _posters[];
-    private String _optionNames[];
-    private String _optionValues[];
-    private Signature _signature;
-
-    public BlogInfo() {}
-    
-    public BlogInfo(SigningPublicKey key, SigningPublicKey posters[], Properties opts) {
-        _optionNames = new String[0];
-        _optionValues = new String[0];
-        setKey(key);
-        setPosters(posters);
-        for (Iterator iter = opts.keySet().iterator(); iter.hasNext(); ) {
-            String k = (String)iter.next();
-            String v = opts.getProperty(k);
-            setProperty(k.trim(), v.trim());
-        }
-    }
-    
-    public SigningPublicKey getKey() { return _key; }
-    public void setKey(SigningPublicKey key) { 
-        _key = key; 
-        setProperty(OWNER_KEY, Base64.encode(key.getData()));
-    }
-    
-    public static final String OWNER_KEY = "Owner";
-    public static final String POSTERS = "Posters";
-    public static final String SIGNATURE = "Signature";
-    public static final String NAME = "Name";
-    public static final String DESCRIPTION = "Description";
-    public static final String CONTACT_URL = "ContactURL";
-    public static final String EDITION = "Edition";
-    public static final String SUMMARY_ENTRY_ID = "SummaryEntryId";
-    
-    public void load(InputStream in) throws IOException {
-        Log log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
-        BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
-        List names = new ArrayList();
-        List vals = new ArrayList();
-        String line = null;
-        while ( (line = reader.readLine()) != null) {
-            if (log.shouldLog(Log.DEBUG))
-                log.debug("Read info line [" + line + "]");
-            line = line.trim();
-            int len = line.length();
-            int split = line.indexOf(':');
-            if ( (len <= 0) || (split <= 0) ) {
-                continue;
-            } else if (split >= len - 1) {
-                names.add(line.substring(0, split).trim());
-                vals.add("");
-                continue;
-            }
-            
-            String key = line.substring(0, split).trim();
-            String val = line.substring(split+1).trim();
-            names.add(key);
-            vals.add(val);
-        }
-        _optionNames = new String[names.size()];
-        _optionValues = new String[names.size()];
-        for (int i = 0; i < _optionNames.length; i++) {
-            _optionNames[i] = (String)names.get(i);
-            _optionValues[i] = (String)vals.get(i);
-            //System.out.println("Loaded info: [" + _optionNames[i] + "] = [" + _optionValues[i] + "]");
-        }
-        
-        String keyStr = getProperty(OWNER_KEY);
-        if (keyStr == null) throw new IOException("Owner not found");
-        _key = new SigningPublicKey(Base64.decode(keyStr));
-        
-        String postersStr = getProperty(POSTERS);
-        if (postersStr != null) {
-            StringTokenizer tok = new StringTokenizer(postersStr, ", \t");
-            _posters = new SigningPublicKey[tok.countTokens()];
-            for (int i = 0; tok.hasMoreTokens(); i++)
-                _posters[i] = new SigningPublicKey(Base64.decode(tok.nextToken()));
-        }
-        
-        String sigStr = getProperty(SIGNATURE);
-        if (sigStr == null) throw new IOException("Signature not found");
-        _signature = new Signature(Base64.decode(sigStr));
-    }
-    
-    public void write(OutputStream out) throws IOException { write(out, true); }
-    public void write(OutputStream out, boolean includeRealSignature) throws IOException {
-        StringBuffer buf = new StringBuffer(512);
-        for (int i = 0; i < _optionNames.length; i++) {
-            if ( (includeRealSignature) || (!SIGNATURE.equals(_optionNames[i])) )
-                buf.append(_optionNames[i]).append(':').append(_optionValues[i]).append('\n');
-        }
-        String s = buf.toString();
-        out.write(s.getBytes("UTF-8"));
-    }
-    
-    public String getProperty(String name) {
-        for (int i = 0; i < _optionNames.length; i++) {
-            if (_optionNames[i].equals(name)) {
-                String val = _optionValues[i];
-                //System.out.println("getProperty[" + name + "] = [" + val + "] [sz=" + val.length() +"]");
-                //for (int j = 0; j < val.length(); j++) {
-                //    char c = (char)val.charAt(j);
-                //    if (c != (c & 0x7F))
-                //        System.out.println("char " + j + ": " + (int)c);
-                //}
-                return val;
-            }
-        }
-        return null;
-    }
-    
-    private void setProperty(String name, String val) {
-        for (int i = 0; i < _optionNames.length; i++) {
-            if (_optionNames[i].equals(name)) {
-                _optionValues[i] = val;
-                return;
-            }
-        }
-            
-        String names[] = new String[_optionNames.length + 1];
-        String values[] = new String[_optionValues.length + 1];
-        for (int i = 0; i < _optionNames.length; i++) {
-            names[i] = _optionNames[i];
-            values[i] = _optionValues[i];
-        }
-        names[names.length-1] = name;
-        values[values.length-1] = val;
-        _optionNames = names;
-        _optionValues = values;
-    }
-    
-    public int getEdition() { 
-        String e = getProperty(EDITION);
-        if (e != null) {
-            try {
-                return Integer.parseInt(e);
-            } catch (NumberFormatException nfe) {
-                return 0;
-            }
-        }
-        return 0;
-    }
-    
-    public String[] getProperties() { return _optionNames; }
-    
-    public SigningPublicKey[] getPosters() { return _posters; }
-    public void setPosters(SigningPublicKey posters[]) { 
-        _posters = posters; 
-        StringBuffer buf = new StringBuffer();
-        for (int i = 0; posters != null && i < posters.length; i++) {
-            buf.append(Base64.encode(posters[i].getData()));
-            if (i + 1 < posters.length)
-                buf.append(',');
-        }
-        setProperty(POSTERS, buf.toString());
-    }
-    
-    public boolean verify(I2PAppContext ctx) {
-        try {
-            ByteArrayOutputStream out = new ByteArrayOutputStream(512);
-            write(out, false);
-            out.close();
-            byte data[] = out.toByteArray();
-            return ctx.dsa().verifySignature(_signature, data, _key);
-        } catch (IOException ioe) {
-            return false;
-        }
-    }
-    
-    public void sign(I2PAppContext ctx, SigningPrivateKey priv) {
-        try {
-            ByteArrayOutputStream out = new ByteArrayOutputStream(512);
-            write(out, false);
-            byte data[] = out.toByteArray();
-            Signature sig = ctx.dsa().sign(data, priv);
-            if (sig == null)
-                throw new IOException("wtf, why is the signature null? data.len = " + data.length + " priv: " + priv);
-            setProperty(SIGNATURE, Base64.encode(sig.getData()));
-            _signature = sig;
-        } catch (IOException ioe) {
-            ioe.printStackTrace();
-        }
-    }
-    
-    public String toString() {
-        StringBuffer buf = new StringBuffer();
-        buf.append("Blog ").append(getKey().calculateHash().toBase64());
-        for (int i = 0; i < _optionNames.length; i++) {
-            if ( (!SIGNATURE.equals(_optionNames[i])) &&
-                 (!OWNER_KEY.equals(_optionNames[i])) &&
-                 (!SIGNATURE.equals(_optionNames[i])) )
-                buf.append(' ').append(_optionNames[i]).append(": ").append(_optionValues[i]);
-        }
-
-        if ( (_posters != null) && (_posters.length > 0) ) {
-            buf.append(" additional posts by");
-            for (int i = 0; i < _posters.length; i++) {
-                buf.append(' ').append(_posters[i].calculateHash().toBase64());
-                if (i + 1 < _posters.length)
-                    buf.append(',');
-            }
-        }
-        return buf.toString();
-    }
-
-    private static final String TEST_STRING = "\u20AC\u00DF\u6771\u10400\u00F6";
-    
-    public static void main(String args[]) {
-        I2PAppContext ctx = I2PAppContext.getGlobalContext();
-        if (true) {
-        try {
-            Object keys[] = ctx.keyGenerator().generateSigningKeypair();
-            SigningPublicKey pub = (SigningPublicKey)keys[0];
-            SigningPrivateKey priv = (SigningPrivateKey)keys[1];
-
-            Properties opts = new Properties();
-            opts.setProperty("Name", TEST_STRING);
-            opts.setProperty("Description", TEST_STRING);
-            opts.setProperty("Edition", "0");
-            opts.setProperty("ContactURL", TEST_STRING);
-
-            String nameOrig = opts.getProperty("Name");
-            BlogInfo info = new BlogInfo(pub, null, opts);
-            info.sign(ctx, priv);
-            boolean ok = info.verify(ctx);
-            System.err.println("sign&verify: " + ok);
-            
-            FileOutputStream o = new FileOutputStream("bloginfo-test.dat");
-            info.write(o, true);
-            o.close();
-            FileInputStream i = new FileInputStream("bloginfo-test.dat");
-            byte buf[] = new byte[4096];
-            int sz = DataHelper.read(i, buf);
-            BlogInfo read = new BlogInfo();
-            read.load(new ByteArrayInputStream(buf, 0, sz));
-            ok = read.verify(ctx);
-            System.err.println("write to disk, verify read: " + ok);
-            System.err.println("Data: " + Base64.encode(buf, 0, sz));
-            System.err.println("Str : " + new String(buf, 0, sz));
-            
-            System.err.println("Name ok? " + read.getProperty("Name").equals(TEST_STRING));
-            System.err.println("Desc ok? " + read.getProperty("Description").equals(TEST_STRING));
-            System.err.println("Name ok? " + read.getProperty("ContactURL").equals(TEST_STRING));
-        } catch (Exception e) { e.printStackTrace(); }
-        } else {
-        try {
-            FileInputStream in = new FileInputStream(args[0]);
-            BlogInfo info = new BlogInfo();
-            info.load(in);
-            boolean ok = info.verify(I2PAppContext.getGlobalContext());
-            System.out.println("OK? " + ok + " :" + info);
-        } catch (Exception e) { e.printStackTrace(); }
-        }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/data/BlogInfoData.java b/apps/syndie/java/src/net/i2p/syndie/data/BlogInfoData.java
deleted file mode 100644
index 8cda08be3cc79235404b7385c1744195f39cab68..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/data/BlogInfoData.java
+++ /dev/null
@@ -1,135 +0,0 @@
-package net.i2p.syndie.data;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Properties;
-
-import net.i2p.client.naming.PetName;
-import net.i2p.data.DataHelper;
-
-/**
- * Contain the current supplementary data for rendering a blog, as opposed to
- * just verifying and rendering a post.
- */
-public class BlogInfoData {
-    private BlogURI _dataEntryId;
-    /** list of List of PetName instances that the blog refers to */
-    private List _referenceGroups;
-    /** customized style config */
-    private Properties _styleOverrides;
-    /** the blog's logo */
-    private Attachment _logo;
-    private List _otherAttachments;
-    
-    public static final String ATTACHMENT_LOGO = "logo.png";
-    public static final String ATTACHMENT_REFERENCE_GROUPS = "groups.txt";
-    public static final String ATTACHMENT_STYLE_OVERRIDE = "style.cfg";
-    /** identifies a post as being a blog info data, not a content bearing post */
-    public static final String TAG = "BlogInfoData";
-
-    public static final int MAX_LOGO_SIZE = 128*1024;
-    
-    public BlogInfoData() {}
-    
-    public BlogURI getEntryId() { return _dataEntryId; }
-    public boolean isLogoSpecified() { return _logo != null; }
-    public Attachment getLogo() { return _logo; }
-    public boolean isStyleSpecified() { return _styleOverrides != null; }
-    public Properties getStyleOverrides() { return _styleOverrides; }
-    public int getReferenceGroupCount() { return _referenceGroups != null ? _referenceGroups.size() : 0; }
-    /** list of PetName elements to be included in the list */
-    public List getReferenceGroup(int groupNum) { return (List)_referenceGroups.get(groupNum); }
-    public int getOtherAttachmentCount() { return _otherAttachments != null ? _otherAttachments.size() : 0; }
-    public Attachment getOtherAttachment(int num) { return (Attachment)_otherAttachments.get(num); }
-    public Attachment getOtherAttachment(String name) {
-        for (int i = 0; i < _otherAttachments.size(); i++) {
-            Attachment a = (Attachment)_otherAttachments.get(i);
-            if (a.getName().equals(name))
-                return a;
-        }
-        return null;
-    }
-    
-    public void writeLogo(OutputStream out) throws IOException {
-        InputStream in = null;
-        try {
-            in = _logo.getDataStream();
-            byte buf[] = new byte[4096];
-            int read = 0;
-            while ( (read = in.read(buf)) != -1)
-                out.write(buf, 0, read);
-        } finally {
-            if (in != null) try { in.close(); } catch (IOException ioe) {}
-        }
-    }
-
-    
-    public void load(EntryContainer entry) throws IOException {
-        _dataEntryId = entry.getURI();
-        Attachment attachments[] = entry.getAttachments();
-        for (int i = 0; i < attachments.length; i++) {
-            if (ATTACHMENT_LOGO.equals(attachments[i].getName())) {
-                _logo = attachments[i];
-            } else if (ATTACHMENT_REFERENCE_GROUPS.equals(attachments[i].getName())) {
-                readReferenceGroups(attachments[i]);
-            } else if (ATTACHMENT_STYLE_OVERRIDE.equals(attachments[i].getName())) {
-                readStyleOverride(attachments[i]);
-            } else {
-                if (_otherAttachments == null)
-                    _otherAttachments = new ArrayList();
-                _otherAttachments.add(attachments[i]);
-            }
-        }
-    }
-    
-    private void readReferenceGroups(Attachment att) throws IOException {
-        InputStream in = null;
-        try {
-            in = att.getDataStream();
-            StringBuffer line = new StringBuffer(128);
-            List groups = new ArrayList();
-            String prevGroup = null;
-            List defaultGroup = new ArrayList();
-            while (true) {
-                boolean ok = DataHelper.readLine(in, line);
-                if (line.length() > 0) {
-                    PetName pn = new PetName(line.toString().trim());
-                    if (pn.getGroupCount() <= 0) {
-                        defaultGroup.add(pn);
-                    } else if (pn.getGroup(0).equals(prevGroup)) {
-                        List curGroup = (List)groups.get(groups.size()-1);
-                        curGroup.add(pn);
-                    } else {
-                        List curGroup = new ArrayList();
-                        curGroup.add(pn);
-                        groups.add(curGroup);
-                        prevGroup = pn.getGroup(0);
-                    }
-                }
-                line.setLength(0);
-                if (!ok)
-                    break;
-            }
-            if (defaultGroup.size() > 0)
-                groups.add(defaultGroup);
-            _referenceGroups = groups;
-        } finally {
-            if (in != null) try { in.close(); } catch (IOException ioe) {}
-        }
-    }
-    
-    private void readStyleOverride(Attachment att) throws IOException {
-        InputStream in = null;
-        try {
-            in = att.getDataStream();
-            Properties props = new Properties();
-            DataHelper.loadProps(props, in);
-            _styleOverrides = props;
-        } finally {
-            if (in != null) try { in.close(); } catch (IOException ioe) {}
-        }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/data/BlogURI.java b/apps/syndie/java/src/net/i2p/syndie/data/BlogURI.java
deleted file mode 100644
index a164cb32c420b80cdd016fc7700eb7edb6dc84b5..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/data/BlogURI.java
+++ /dev/null
@@ -1,119 +0,0 @@
-package net.i2p.syndie.data;
-
-import java.util.Comparator;
-
-import net.i2p.data.Base64;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Hash;
-
-/**
- *
- */
-public class BlogURI {
-    private Hash _blogHash;
-    private long _entryId;
-    
-    public static final Comparator COMPARATOR = new NewestFirstComparator();
-    
-    public BlogURI() {
-        this(null, -1);
-    }
-    public BlogURI(Hash blogHash, long entryId) {
-        _blogHash = blogHash;
-        _entryId = entryId;
-    }
-    public BlogURI(String uri) {
-        if (uri.startsWith("blog://")) {
-            int off = "blog://".length();
-            _blogHash = new Hash(Base64.decode(uri.substring(off, off+44))); // 44 chars == base64(32 bytes)
-            int entryStart = uri.indexOf('/', off+1);
-            if (entryStart < 0) {
-                _entryId = -1;
-            } else {
-                try {
-                    _entryId = Long.parseLong(uri.substring(entryStart+1).trim());
-                } catch (NumberFormatException nfe) {
-                    _entryId = -1;
-                }
-            }
-        } else if (uri.startsWith("entry://")) {
-            int off = "entry://".length();
-            _blogHash = new Hash(Base64.decode(uri.substring(off, off+44))); // 44 chars == base64(32 bytes)
-            int entryStart = uri.indexOf('/', off+1);
-            if (entryStart < 0) {
-                _entryId = -1;
-            } else {
-                try {
-                    _entryId = Long.parseLong(uri.substring(entryStart+1).trim());
-                } catch (NumberFormatException nfe) {
-                    _entryId = -1;
-                }
-            }
-        } else {
-            _blogHash = null;
-            _entryId = -1;
-        }
-    }
-    
-    public Hash getKeyHash() { return _blogHash; }
-    public long getEntryId() { return _entryId; }
-    
-    public void setKeyHash(Hash hash) { _blogHash = hash; }
-    public void setEntryId(long id) { _entryId = id; }
-    
-    public String toString() {
-        if ( (_blogHash == null) || (_blogHash.getData() == null) )
-            return "";
-        StringBuffer rv = new StringBuffer(64);
-        rv.append("blog://").append(Base64.encode(_blogHash.getData()));
-        rv.append('/');
-        if (_entryId >= 0)
-            rv.append(_entryId);
-        return rv.toString();
-    }
-    
-    public boolean equals(Object obj) {
-        if (obj == null) return false;
-        if (obj.getClass() != getClass()) return false;
-        return DataHelper.eq(_entryId, ((BlogURI)obj)._entryId) && 
-               DataHelper.eq(_blogHash, ((BlogURI)obj)._blogHash);
-    }
-    public int hashCode() {
-        int rv = (int)((_entryId >>> 32) & 0x7FFFFFFF);
-        rv += (_entryId & 0x7FFFFFFF);
-        
-        if (_blogHash != null)
-            rv += _blogHash.hashCode();
-        return rv;
-    }
-    
-    public static void main(String args[]) {
-        test("http://asdf/");
-        test("blog://Vq~AlW-r7OM763okVUFIDvVFzxOjpNNsAx0rFb2yaE8=");
-        test("blog://Vq~AlW-r7OM763okVUFIDvVFzxOjpNNsAx0rFb2yaE8=/");
-        test("blog://Vq~AlW-r7OM763okVUFIDvVFzxOjpNNsAx0rFb2yaE8=/123456789");
-        test("entry://Vq~AlW-r7OM763okVUFIDvVFzxOjpNNsAx0rFb2yaE8=/");
-        test("entry://Vq~AlW-r7OM763okVUFIDvVFzxOjpNNsAx0rFb2yaE8=/123456789");
-    }
-    private static void test(String uri) {
-        BlogURI u = new BlogURI(uri);
-        if (!u.toString().equals(uri))
-            System.err.println("Not a match: [" + uri + "] != [" + u.toString() + "]");
-    }
-
-    /**
-     * Order the BlogURIs by entryId, with the highest entryId first
-     */
-    private static class NewestFirstComparator implements Comparator {        
-        public int compare(Object lhs, Object rhs) {
-            BlogURI l = (BlogURI)lhs;
-            BlogURI r = (BlogURI)rhs;
-            if (l.getEntryId() > r.getEntryId())
-                return -1;
-            else if (l.getEntryId() < r.getEntryId())
-                return 1;
-            else // same date, compare by blog hash (aka randomly)
-                return DataHelper.compareTo(l.getKeyHash().getData(), r.getKeyHash().getData());
-        }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/data/EncodingTestGenerator.java b/apps/syndie/java/src/net/i2p/syndie/data/EncodingTestGenerator.java
deleted file mode 100644
index 7bc0cae36bd0750ff08ccd42acd4e4236b600259..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/data/EncodingTestGenerator.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package net.i2p.syndie.data;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.util.Properties;
-
-import net.i2p.I2PAppContext;
-import net.i2p.data.DataHelper;
-import net.i2p.data.SigningPrivateKey;
-import net.i2p.data.SigningPublicKey;
-
-/**
- * Create a new blog metadata & set of entries using some crazy UTF8 encoded chars,
- * then make sure they're always valid.  These blogs & entries can then be fed into
- * jetty/syndie/etc to see how and where they are getting b0rked.
- */
-public class EncodingTestGenerator {
-    public EncodingTestGenerator() {}
-    public static final String TEST_STRING = "\u20AC\u00DF\u6771\u10400\u00F6";
-    
-    public static void main(String args[]) {
-        I2PAppContext ctx = I2PAppContext.getGlobalContext();
-        try {
-            Object keys[] = ctx.keyGenerator().generateSigningKeypair();
-            SigningPublicKey pub = (SigningPublicKey)keys[0];
-            SigningPrivateKey priv = (SigningPrivateKey)keys[1];
-
-            Properties opts = new Properties();
-            opts.setProperty("Name", TEST_STRING);
-            opts.setProperty("Description", TEST_STRING);
-            opts.setProperty("Edition", "0");
-            opts.setProperty("ContactURL", TEST_STRING);
-
-            String nameOrig = opts.getProperty("Name");
-            BlogInfo info = new BlogInfo(pub, null, opts);
-            info.sign(ctx, priv);
-            boolean ok = info.verify(ctx);
-            System.err.println("sign&verify: " + ok);
-            
-            FileOutputStream o = new FileOutputStream("encodedMeta.dat");
-            info.write(o, true);
-            o.close();
-            FileInputStream i = new FileInputStream("encodedMeta.dat");
-            byte buf[] = new byte[4096];
-            int sz = DataHelper.read(i, buf);
-            BlogInfo read = new BlogInfo();
-            read.load(new ByteArrayInputStream(buf, 0, sz));
-            ok = read.verify(ctx);
-            System.err.println("write to disk, verify read: " + ok);
-            System.err.println("Name ok? " + read.getProperty("Name").equals(TEST_STRING));
-            System.err.println("Desc ok? " + read.getProperty("Description").equals(TEST_STRING));
-            System.err.println("Name ok? " + read.getProperty("ContactURL").equals(TEST_STRING));
-            
-            // ok now lets create some entries
-            BlogURI uri = new BlogURI(read.getKey().calculateHash(), 0);
-            String tags[] = new String[4];
-            for (int j = 0; j < tags.length; j++)
-                tags[j] = TEST_STRING + "_" + j;
-            StringBuffer smlOrig = new StringBuffer(512);
-            smlOrig.append("Subject: ").append(TEST_STRING).append("\n\n");
-            smlOrig.append("Hi with ").append(TEST_STRING);
-            EntryContainer container = new EntryContainer(uri, tags, DataHelper.getUTF8(smlOrig));
-            container.seal(ctx, priv, null);
-            ok = container.verifySignature(ctx, read);
-            System.err.println("Sealed and verified entry: " + ok);
-            FileOutputStream fos = new FileOutputStream("encodedEntry.dat");
-            container.write(fos, true);
-            fos.close();
-            System.out.println("Written to " + new File("encodedEntry.dat").getAbsolutePath());
-            
-            FileInputStream fis = new FileInputStream("encodedEntry.dat");
-            EntryContainer read2 = new EntryContainer();
-            read2.load(fis);
-            ok = read2.verifySignature(ctx, read);
-            System.out.println("Read ok? " + ok);
-            
-            read2.parseRawData(ctx);
-            String tagsRead[] = read2.getTags();
-            for (int j = 0; j < tagsRead.length; j++) {
-                if (!tags[j].equals(tagsRead[j]))
-                    System.err.println("Tag error [" + j + "]: read = [" + tagsRead[j] + "] want [" + tags[j] + "]");
-                else
-                    System.err.println("Tag ok [" + j + "]");
-            }
-            String readText = read2.getEntry().getText();
-            ok = readText.equals(smlOrig.toString());
-            System.err.println("SML text ok? " + ok);
-        } catch (Exception e) { e.printStackTrace(); }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/data/Entry.java b/apps/syndie/java/src/net/i2p/syndie/data/Entry.java
deleted file mode 100644
index 55e23f133b0534c7b10173773f987d2e21a80357..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/data/Entry.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package net.i2p.syndie.data;
-
-/**
- *
- */
-public class Entry {
-    private String _text;
-    
-    public Entry(String raw) {
-        _text = raw;
-    }
-    
-    public String getText() { return _text; }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/data/EntryContainer.java b/apps/syndie/java/src/net/i2p/syndie/data/EntryContainer.java
deleted file mode 100644
index 892d58adecdb5549554604ea17d24d2ec551117f..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/data/EntryContainer.java
+++ /dev/null
@@ -1,460 +0,0 @@
-package net.i2p.syndie.data;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-import java.util.zip.ZipOutputStream;
-
-import net.i2p.I2PAppContext;
-import net.i2p.data.Base64;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Hash;
-import net.i2p.data.SessionKey;
-import net.i2p.data.Signature;
-import net.i2p.data.SigningPrivateKey;
-import net.i2p.util.Log;
-
-/**
- * Securely wrap up an entry and any attachments.  Container format:<pre>
- * $format\n
- * [$key: $val\n]*
- * \n
- * Signature: $base64(DSA signature)\n
- * Size: sizeof(data)\n
- * [data bytes]
- * </pre>
- *
- * Required keys: 
- *  BlogKey: base64 of the SHA256 of the blog's public key
- *  BlogTags: tab delimited list of tags under which this entry should be organized
- *  BlogEntryId: base10 unique identifier of this entry within the key/path.  Typically starts
- *               as the current day (in unix time, milliseconds) plus further milliseconds for
- *               each entry within the day.
- *
- * The data bytes contains zip file, either in the clear or encrypted.  If the format
- * is encrypted, the BlogPath key will (likely) be encrypted as well.
- * 
- */
-public class EntryContainer {
-    private List _rawKeys;
-    private List _rawValues;
-    private Signature _signature;
-    private byte _rawData[];
-    
-    private BlogURI _entryURI;
-    private int _format;
-    private Entry _entryData;
-    private Attachment _attachments[];
-    private int _completeSize;
-    
-    public static final int FORMAT_ZIP_UNENCRYPTED = 0;
-    public static final int FORMAT_ZIP_ENCRYPTED = 1;
-    public static final String FORMAT_ZIP_UNENCRYPTED_STR = "syndie.entry.zip-unencrypted";
-    public static final String FORMAT_ZIP_ENCRYPTED_STR = "syndie.entry.zip-encrypted";
-    
-    public static final String HEADER_BLOGKEY = "BlogKey";
-    public static final String HEADER_BLOGTAGS = "BlogTags";
-    public static final String HEADER_ENTRYID = "BlogEntryId";
-    
-    public EntryContainer() {
-        _rawKeys = new ArrayList();
-        _rawValues = new ArrayList();
-        _completeSize = -1;
-    }
-    
-    public EntryContainer(BlogURI uri, String tags[], byte smlData[]) {
-        this();
-        _entryURI = uri;
-        if ( (smlData == null) || (smlData.length <= 0) )
-            _entryData = new Entry(""); //null);
-        else
-            _entryData = new Entry(DataHelper.getUTF8(smlData));
-        setHeader(HEADER_BLOGKEY, Base64.encode(uri.getKeyHash().getData()));
-        StringBuffer buf = new StringBuffer();
-        for (int i = 0; tags != null && i < tags.length; i++)
-            buf.append(tags[i]).append('\t');
-        setHeader(HEADER_BLOGTAGS, buf.toString());
-        if (uri.getEntryId() < 0)
-            uri.setEntryId(System.currentTimeMillis());
-        setHeader(HEADER_ENTRYID, Long.toString(uri.getEntryId()));
-    }
-    
-    public int getFormat() { return _format; }
-    
-    private String readLine(InputStream in) throws IOException {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
-        int i = 0;
-        while (true) {
-            int c = in.read();
-            if ( (c == (int)'\n') || (c == (int)'\r') ) {
-                break;
-            } else if (c == -1) {
-                if (i == 0)
-                    return null;
-                else
-                    break;
-            } else {
-                baos.write(c);
-            }
-            i++;
-        }
-        
-        return DataHelper.getUTF8(baos.toByteArray());
-        //BufferedReader r = new BufferedReader(new InputStreamReader(in, "UTF-8"), 1);
-        //String line = r.readLine();
-        //return line;
-    }
-    
-    public void load(InputStream source) throws IOException {
-        String line = readLine(source);
-        if (line == null) throw new IOException("No format line in the entry");
-        //System.err.println("read container format line [" + line + "]");
-        String fmt = line.trim();
-        if (FORMAT_ZIP_UNENCRYPTED_STR.equals(fmt)) {
-            _format = FORMAT_ZIP_UNENCRYPTED;
-        } else if (FORMAT_ZIP_ENCRYPTED_STR.equals(fmt)) {
-            _format = FORMAT_ZIP_ENCRYPTED;
-        } else {
-            throw new IOException("Unsupported entry format: " + fmt);
-        }
-        
-        while ( (line = readLine(source)) != null) {
-            //System.err.println("read container header line [" + line + "]");
-            line = line.trim();
-            int len = line.length();
-            if (len <= 0)
-                break;
-            int split = line.indexOf(':');
-            if (split <= 0) {
-                throw new IOException("Invalid format of the syndie entry: line=" + line);
-            } else if (split >= len - 2) {
-                // foo:\n
-                String key = line.substring(0, split);
-                _rawKeys.add(key);
-                _rawValues.add("");
-            } else {
-                String key = line.substring(0, split);
-                String val = line.substring(split+1);
-                _rawKeys.add(key);
-                _rawValues.add(val);
-            }
-        }
-        
-        parseHeaders();
-        
-        String sigStr = readLine(source); 
-        //System.err.println("read container signature line [" + line + "]");
-        if ( (sigStr == null) || (sigStr.indexOf("Signature:") == -1) )
-            throw new IOException("No signature line");
-        sigStr = sigStr.substring("Signature:".length()+1).trim();
-        
-        _signature = new Signature(Base64.decode(sigStr));
-        //System.out.println("Sig: " + _signature.toBase64());
-        
-        line = readLine(source); 
-        //System.err.println("read container size line [" + line + "]");
-        if (line == null)
-            throw new IOException("No size line");
-        line = line.trim();
-        int dataSize = -1;
-        try {
-            int index = line.indexOf("Size:");
-            if (index == 0)
-                dataSize = Integer.parseInt(line.substring("Size:".length()+1).trim());
-            else
-                throw new IOException("Invalid size line");
-        } catch (NumberFormatException nfe) {
-            throw new IOException("Invalid entry size: " + line);
-        }
-        
-        byte data[] = new byte[dataSize];
-        int read = DataHelper.read(source, data);
-        if (read != dataSize)
-            throw new IOException("Incomplete entry: read " + read + " expected " + dataSize);
-        
-        _rawData = data;
-    }
-    
-    public void seal(I2PAppContext ctx, SigningPrivateKey signingKey, SessionKey entryKey) throws IOException {
-        Log l = ctx.logManager().getLog(getClass());
-        if (l.shouldLog(Log.DEBUG))
-            l.debug("Sealing " + _entryURI);
-        if (entryKey == null)
-            _format = FORMAT_ZIP_UNENCRYPTED;
-        else
-            _format = FORMAT_ZIP_ENCRYPTED;
-        setHeader(HEADER_BLOGKEY, Base64.encode(_entryURI.getKeyHash().getData()));
-        if (_entryURI.getEntryId() < 0)
-            _entryURI.setEntryId(ctx.clock().now());
-        setHeader(HEADER_ENTRYID, Long.toString(_entryURI.getEntryId()));
-        _rawData = createRawData(ctx, entryKey);
-        ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
-        write(baos, false);
-        byte data[] = baos.toByteArray();
-        _signature = ctx.dsa().sign(data, signingKey);
-    }
-    
-    private byte[] createRawData(I2PAppContext ctx, SessionKey entryKey) throws IOException {
-        byte raw[] = createRawData();
-        if (entryKey != null) {
-            byte iv[] = new byte[16];
-            ctx.random().nextBytes(iv);
-            byte rv[] = new byte[raw.length + iv.length];
-            ctx.aes().encrypt(raw, 0, rv, iv.length, entryKey, iv, raw.length);
-            System.arraycopy(iv, 0, rv, 0, iv.length);
-            return rv;
-        } else {
-            return raw;
-        }
-    }
-    
-    private byte[] createRawData() throws IOException {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        ZipOutputStream out = new ZipOutputStream(baos);
-        ZipEntry ze = new ZipEntry(ZIP_ENTRY);
-        byte data[] = null;
-        if (_entryData.getText() != null)
-            data = DataHelper.getUTF8(_entryData.getText());
-        ze.setTime(0);
-        out.putNextEntry(ze);
-        if (data != null)
-            out.write(data);
-        out.closeEntry();
-        for (int i = 0; (_attachments != null) && (i < _attachments.length); i++) {
-            ze = new ZipEntry(ZIP_ATTACHMENT_PREFIX + i + ZIP_ATTACHMENT_SUFFIX);
-            data = _attachments[i].getData();
-            out.putNextEntry(ze);
-            out.write(data);
-            out.closeEntry();
-            ze = new ZipEntry(ZIP_ATTACHMENT_META_PREFIX + i + ZIP_ATTACHMENT_META_SUFFIX);
-            data = _attachments[i].getRawMetadata();
-            out.putNextEntry(ze);
-            out.write(data);
-            out.closeEntry();
-        }
-        out.finish();
-        out.close();
-        return baos.toByteArray();
-    }
-    
-    public static final String ZIP_ENTRY = "entry.sml";
-    public static final String ZIP_ATTACHMENT_PREFIX = "attachmentdata";
-    public static final String ZIP_ATTACHMENT_SUFFIX = ".szd";
-    public static final String ZIP_ATTACHMENT_META_PREFIX = "attachmentmeta";
-    public static final String ZIP_ATTACHMENT_META_SUFFIX = ".szm";
-    
-    public void parseRawData(I2PAppContext ctx) throws IOException { parseRawData(ctx, null); }
-    public void parseRawData(I2PAppContext ctx, SessionKey zipKey) throws IOException {
-        int dataOffset = 0;
-        if (zipKey != null) {
-            byte iv[] = new byte[16];
-            System.arraycopy(_rawData, 0, iv, 0, iv.length);
-            ctx.aes().decrypt(_rawData, iv.length, _rawData, iv.length, zipKey, iv, _rawData.length - iv.length);
-            dataOffset = iv.length;
-        }
-        
-        ByteArrayInputStream in = new ByteArrayInputStream(_rawData, dataOffset, _rawData.length - dataOffset);
-        ZipInputStream zi = new ZipInputStream(in);
-        Map attachments = new HashMap();
-        Map attachmentMeta = new HashMap();
-        while (true) {
-            ZipEntry entry = zi.getNextEntry();
-            if (entry == null)
-                break;
-            
-            ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
-            byte buf[] = new byte[1024];
-            int read = -1;
-            while ( (read = zi.read(buf)) != -1) 
-                out.write(buf, 0, read);
-            
-            byte entryData[] = out.toByteArray();
-            
-            String name = entry.getName();
-            if (ZIP_ENTRY.equals(name)) {
-                _entryData = new Entry(DataHelper.getUTF8(entryData));
-            } else if (name.startsWith(ZIP_ATTACHMENT_PREFIX)) {
-                attachments.put(name, (Object)entryData);
-            } else if (name.startsWith(ZIP_ATTACHMENT_META_PREFIX)) {
-                attachmentMeta.put(name, (Object)entryData);
-            }
-            
-            //System.out.println("Read entry [" + name + "] with size=" + entryData.length);
-        }
-        
-        if (_entryData == null)
-            _entryData = new Entry(""); //null);
-        
-        _attachments = new Attachment[attachments.size()];
-        
-        for (int i = 0; i < attachments.size(); i++) {
-            byte data[] = (byte[])attachments.get(ZIP_ATTACHMENT_PREFIX + i + ZIP_ATTACHMENT_SUFFIX);
-            byte metadata[] = (byte[])attachmentMeta.get(ZIP_ATTACHMENT_META_PREFIX + i + ZIP_ATTACHMENT_META_SUFFIX);
-            if ( (data != null) && (metadata != null) ) {
-                _attachments[i] = new Attachment(data, metadata);
-            } else {
-                Log l = ctx.logManager().getLog(getClass());
-                if (l.shouldLog(Log.WARN))
-                    l.warn("Unable to get " + i + ": " + data + "/" + metadata);
-            }
-        }
-        
-        //System.out.println("Attachments: " + _attachments.length + "/" + attachments.size() + ": " + attachments);
-    }
-    
-    public BlogURI getURI() { return _entryURI; }
-    public static final String NO_TAGS_TAG = "[none]";
-    private static final String NO_TAGS[] = new String[] { NO_TAGS_TAG };
-    public String[] getTags() { 
-        String tags = getHeader(HEADER_BLOGTAGS);
-        if ( (tags == null) || (tags.trim().length() <= 0) ) {
-            return NO_TAGS;
-        } else {
-            StringTokenizer tok = new StringTokenizer(tags, "\t");
-            String rv[] = new String[tok.countTokens()];
-            for (int i = 0; i < rv.length; i++)
-                rv[i] = tok.nextToken().trim();
-            return rv;
-        }
-    }
-    public Signature getSignature() { return _signature; }
-    public Entry getEntry() { return _entryData; }
-    public Attachment[] getAttachments() { return _attachments; }
-    
-    public void setCompleteSize(int bytes) { _completeSize = bytes; }
-    public int getCompleteSize() { return _completeSize; }
-    
-    public String getHeader(String key) {
-        for (int i = 0; i < _rawKeys.size(); i++) {
-            String k = (String)_rawKeys.get(i);
-            if (k.equals(key))
-                return (String)_rawValues.get(i);
-        }
-        return null;
-    }
-    
-    public Map getHeaders() {
-        Map rv = new HashMap(_rawKeys.size());
-        for (int i = 0; i < _rawKeys.size(); i++) {
-            String k = (String)_rawKeys.get(i);
-            String v = (String)_rawValues.get(i);
-            rv.put(k,v);
-        }
-        return rv;
-    }
-    
-    public void setHeader(String name, String val) {
-        int index = _rawKeys.indexOf(name);
-        if (index < 0) {
-            _rawKeys.add(name);
-            _rawValues.add(val);
-        } else {
-            _rawValues.set(index, val);
-        }
-    }
-    
-    public void addAttachment(byte data[], String name, String description, String mimeType) {
-        Attachment a = new Attachment(data, name, description, mimeType);
-        int old = (_attachments == null ? 0 : _attachments.length);
-        Attachment nv[] = new Attachment[old+1];
-        if (old > 0)
-            for (int i = 0; i < old; i++)
-                nv[i] = _attachments[i];
-        nv[old] = a;
-        _attachments = nv;
-    }
-    
-    private void parseHeaders() throws IOException {
-        String keyHash = getHeader(HEADER_BLOGKEY);
-        String idVal = getHeader(HEADER_ENTRYID);
-        
-        if (keyHash == null) {
-            throw new IOException("Missing " + HEADER_BLOGKEY + " header");
-        }
-        
-        long entryId = -1;
-        if ( (idVal != null) && (idVal.length() > 0) ) {
-            try {
-                entryId = Long.parseLong(idVal.trim());
-            } catch (NumberFormatException nfe) {
-                throw new IOException("Invalid format of entryId (" + idVal + ")");
-            }
-        }
-        
-        _entryURI = new BlogURI(new Hash(Base64.decode(keyHash)), entryId);
-    }
-    
-    public boolean verifySignature(I2PAppContext ctx, BlogInfo info) {
-        if (_signature == null) throw new NullPointerException("sig is null");
-        if (info == null) throw new NullPointerException("info is null");
-        if (info.getKey() == null) throw new NullPointerException("info key is null");
-        if (info.getKey().getData() == null) throw new NullPointerException("info key data is null");
-        //System.out.println("Verifying " + _entryURI + " for " + info);
-        
-        ByteArrayOutputStream out = new ByteArrayOutputStream(_rawData.length + 512);
-        try {
-            write(out, false);
-            byte dat[] = out.toByteArray();
-            //System.out.println("Raw data to verify: " + ctx.sha().calculateHash(dat).toBase64() + " sig: " + _signature.toBase64());
-            ByteArrayInputStream in = new ByteArrayInputStream(dat);
-            boolean ok = ctx.dsa().verifySignature(_signature, in, info.getKey());
-            if (!ok && info.getPosters() != null) {
-                for (int i = 0; !ok && i < info.getPosters().length; i++) {
-                    in.reset();
-                    ok = ctx.dsa().verifySignature(_signature, in, info.getPosters()[i]);
-                }
-            }
-            //System.out.println("Verified ok? " + ok + " key: " + info.getKey().calculateHash().toBase64());
-            //new Exception("verifying").printStackTrace();
-            return ok;
-        } catch (IOException ioe) {
-            //System.out.println("Verification failed! " + ioe.getMessage());
-            return false;
-        }
-    }
-    
-    public void write(OutputStream out, boolean includeRealSignature) throws IOException {
-        StringBuffer buf = new StringBuffer(512);
-        switch (_format) {
-            case FORMAT_ZIP_ENCRYPTED:
-                buf.append(FORMAT_ZIP_ENCRYPTED_STR).append('\n');
-                break;
-            case FORMAT_ZIP_UNENCRYPTED:
-                buf.append(FORMAT_ZIP_UNENCRYPTED_STR).append('\n');
-                break;
-            default:
-                throw new IOException("Invalid format " + _format);
-        }
-        
-        for (int i = 0; i < _rawKeys.size(); i++) {
-            String k = (String)_rawKeys.get(i);
-            buf.append(k.trim());
-            buf.append(": ");
-            buf.append(((String)_rawValues.get(i)).trim());
-            buf.append('\n');
-        }
-        
-        buf.append('\n');
-        buf.append("Signature: ");
-        if (includeRealSignature) 
-            buf.append(Base64.encode(_signature.getData()));
-        buf.append("\n");
-        buf.append("Size: ").append(_rawData.length).append('\n');
-        String str = buf.toString();
-        
-        //System.out.println("Writing raw: \n[" + str + "] / " + I2PAppContext.getGlobalContext().sha().calculateHash(str.getBytes()) + ", raw data: " + I2PAppContext.getGlobalContext().sha().calculateHash(_rawData).toBase64() + "\n");
-        out.write(DataHelper.getUTF8(str));
-        out.write(_rawData);
-    }
-    
-    public String toString() { return _entryURI.toString(); }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/data/FilteredThreadIndex.java b/apps/syndie/java/src/net/i2p/syndie/data/FilteredThreadIndex.java
deleted file mode 100644
index 8cf4a66e9834ce03c43b0c39b569ec2fe0594887..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/data/FilteredThreadIndex.java
+++ /dev/null
@@ -1,129 +0,0 @@
-package net.i2p.syndie.data;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-import net.i2p.client.naming.PetName;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.User;
-
-/**
- *
- */
-public class FilteredThreadIndex extends ThreadIndex {
-    private User _user;
-    private Archive _archive;
-    private ThreadIndex _baseIndex;
-    private Collection _filteredTags;
-    private List _roots;
-    private List _ignoredAuthors;
-    private Collection _filteredAuthors;
-    private boolean _filterAuthorsByRoot;
-
-    public static final String GROUP_FAVORITE = "Favorite";
-    public static final String GROUP_IGNORE = "Ignore";
-
-    public FilteredThreadIndex(User user, Archive archive, Collection tags, Collection authors, boolean filterAuthorsByRoot) {
-        super();
-        _user = user;
-        _archive = archive;
-        _baseIndex = _archive.getIndex().getThreadedIndex();
-        _filteredTags = tags;
-        if (_filteredTags == null)
-            _filteredTags = Collections.EMPTY_SET;
-        _filteredAuthors = authors;
-        if (_filteredAuthors == null)
-            _filteredAuthors = Collections.EMPTY_SET;
-        _filterAuthorsByRoot = filterAuthorsByRoot;
-        
-        _ignoredAuthors = new ArrayList();
-        for (Iterator iter = user.getPetNameDB().iterator(); iter.hasNext(); ) {
-            PetName pn = (PetName)iter.next();
-            if (pn.isMember(GROUP_IGNORE)) {
-                try {
-                    Hash h = new Hash();
-                    h.fromBase64(pn.getLocation());
-                    _ignoredAuthors.add(h);
-                } catch (DataFormatException dfe) {
-                    // ignore
-                }
-            }
-        }
-        
-        filter();
-    }
-    
-    private void filter() {
-        _roots = new ArrayList(_baseIndex.getRootCount());
-        for (int i = 0; i < _baseIndex.getRootCount(); i++) {
-            ThreadNode node = _baseIndex.getRoot(i);
-            if (!isIgnored(node, _ignoredAuthors, _filteredTags, _filteredAuthors, _filterAuthorsByRoot))
-                _roots.add(node);
-        }
-    }
-    
-    private boolean isIgnored(ThreadNode node, List ignoredAuthors, Collection requestedTags, Collection filteredAuthors, boolean filterAuthorsByRoot) {
-        if (node.getTags().contains(BlogInfoData.TAG))
-            return true; // its a fake post, containing some updated metadata for the blog
-        if (filteredAuthors.size() <= 0) {
-            boolean allAuthorsIgnored = true;
-            for (Iterator iter = node.getRecursiveAuthorIterator(); iter.hasNext(); ) {
-                Hash author = (Hash)iter.next();
-                if (!ignoredAuthors.contains(author)) {
-                    allAuthorsIgnored = false;
-                    break;
-                }
-            }
-            
-            if ( (allAuthorsIgnored) && (ignoredAuthors.size() > 0) )
-                return true;
-        } else {
-            boolean filteredAuthorMatches = false;
-            for (Iterator iter = filteredAuthors.iterator(); iter.hasNext(); ) {
-                Hash author = (Hash)iter.next();
-                if (filterAuthorsByRoot) {
-                    if (node.getEntry().getKeyHash().equals(author)) {
-                        filteredAuthorMatches = true;
-                        break;
-                    }
-                } else { 
-                    if (node.containsAuthor(author)) {
-                        filteredAuthorMatches = true;
-                        break;
-                    }
-                }
-            }
-            if (!filteredAuthorMatches)
-                return true;
-        }
-        
-        // ok, author checking passed, so only ignore the thread if tags were specified and the
-        // thread doesn't contain that tag
-        
-        if (requestedTags.size() > 0) {
-            Collection nodeTags = node.getRecursiveTags();
-            for (Iterator iter = requestedTags.iterator(); iter.hasNext(); ) 
-                if (nodeTags.contains(iter.next()))
-                    return false;
-            // authors we aren't ignoring have posted in the thread, but the user is filtering
-            // posts by tags, and this thread doesn't include any of those tags
-            return true;
-        } else {
-            // we aren't filtering by tags, and we haven't been refused by the author
-            // filtering
-            return false;
-        }
-    }
-    
-    public int getRootCount() { return _roots.size(); }
-    public ThreadNode getRoot(int index) { return (ThreadNode)_roots.get(index); }
-    public ThreadNode getNode(BlogURI uri) { return _baseIndex.getNode(uri); }
-    public Collection getFilteredTags() { return _filteredTags; }
-    public Collection getFilteredAuthors() { return _filteredAuthors; }
-    public boolean getFilterAuthorsByRoot() { return _filterAuthorsByRoot; }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/data/LocalArchiveIndex.java b/apps/syndie/java/src/net/i2p/syndie/data/LocalArchiveIndex.java
deleted file mode 100644
index b14f6156a720f4fc91623a5d79aa38a4611ef0cd..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/data/LocalArchiveIndex.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package net.i2p.syndie.data;
-
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Properties;
-import java.util.Set;
-import java.util.TreeSet;
-
-import net.i2p.I2PAppContext;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.util.Log;
-
-/**
- * writable archive index (most are readonly)
- */
-public class LocalArchiveIndex extends ArchiveIndex {
-    private Log _log;
-    public LocalArchiveIndex(I2PAppContext ctx) {
-        super(ctx, false);
-        _log = ctx.logManager().getLog(getClass());
-    }
-    
-    public void setGeneratedOn(long when) { _generatedOn = when; }
-    
-    public void setVersion(String v) { _version = v; }
-    public void setHeaders(Properties headers) { _headers = headers; }
-    public void setHeader(String key, String val) { _headers.setProperty(key, val); }
-    public void setAllBlogs(int count) { _allBlogs = count; }
-    public void setNewBlogs(int count) { _newBlogs = count; }
-    public void setAllEntries(int count) { _allEntries = count; }
-    public void setNewEntries(int count) { _newEntries = count; }
-    public void setTotalSize(long bytes) { _totalSize = bytes; }
-    public void setNewSize(long bytes) { _newSize = bytes; }
-
-    public void addBlog(Hash key, String tag, long lastUpdated) {
-        for (int i = 0; i < _blogs.size(); i++) {
-            BlogSummary s = (BlogSummary)_blogs.get(i);
-            if ( (s.blog.equals(key)) && (s.tag.equals(tag)) ) {
-                s.lastUpdated = Math.max(s.lastUpdated, lastUpdated);
-                return;
-            }
-        }
-        BlogSummary summary = new ArchiveIndex.BlogSummary();
-        summary.blog = key;
-        summary.tag = tag;
-        summary.lastUpdated = lastUpdated;
-        _blogs.add(summary);
-    }
-    
-    public void addBlogEntry(Hash key, String tag, String entry) {
-        for (int i = 0; i < _blogs.size(); i++) {
-            BlogSummary summary = (BlogSummary)_blogs.get(i);
-            if (summary.blog.equals(key) && (summary.tag.equals(tag)) ) {
-                long entryId = Archive.getEntryIdFromIndexName(entry);
-                int kb = Archive.getSizeFromIndexName(entry);
-                if (_log.shouldLog(Log.INFO))
-                    _log.info("Adding entry " + entryId + ", size=" + kb + "KB [" + entry + "]");
-                EntrySummary entrySummary = new EntrySummary(new BlogURI(key, entryId), kb);
-                for (int j = 0; j < summary.entries.size(); j++) {
-                    EntrySummary cur = (EntrySummary)summary.entries.get(j);
-                    if (cur.entry.equals(entrySummary.entry))
-                        return;
-                }
-                summary.entries.add(entrySummary);
-                return;
-            }
-        }
-    }
-    
-    public void addNewestBlog(Hash key) { 
-        if (!_newestBlogs.contains(key))
-            _newestBlogs.add(key); 
-    }
-    public void addNewestEntry(BlogURI entry) { 
-        if (!_newestEntries.contains(entry))
-            _newestEntries.add(entry); 
-    }
-
-    public void addReply(BlogURI parent, BlogURI reply) {
-        Set replies = (Set)_replies.get(parent);
-        if (replies == null) {
-            replies = Collections.synchronizedSet(new TreeSet(BlogURIComparator.HIGHEST_ID_FIRST));
-            _replies.put(parent, replies);
-        }
-        replies.add(reply);
-        //System.err.println("Adding reply to " + parent + " from child " + reply + " (# replies: " + replies.size() + ")");
-    }
-
-    private static class BlogURIComparator implements Comparator {
-        public static final BlogURIComparator HIGHEST_ID_FIRST = new BlogURIComparator(true);
-        public static final BlogURIComparator HIGHEST_ID_LAST = new BlogURIComparator(false);
-        private boolean _highestFirst;
-        public BlogURIComparator(boolean highestFirst) {
-            _highestFirst = highestFirst;
-        }
-        
-        public int compare(Object lhs, Object rhs) {
-            if ( (lhs == null) || !(lhs instanceof BlogURI) ) return 1;
-            if ( (rhs == null) || !(rhs instanceof BlogURI) ) return -1;
-            BlogURI l = (BlogURI)lhs;
-            BlogURI r = (BlogURI)rhs;
-            if (l.getEntryId() > r.getEntryId())
-                return (_highestFirst ? 1 : -1);
-            else if (l.getEntryId() < r.getEntryId())
-                return (_highestFirst ? -1 : 1);
-            else
-                return DataHelper.compareTo(l.getKeyHash().getData(), r.getKeyHash().getData());
-        }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/data/SafeURL.java b/apps/syndie/java/src/net/i2p/syndie/data/SafeURL.java
deleted file mode 100644
index 31fec23f2901979cc0b36cb9b5512f17877bbe12..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/data/SafeURL.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package net.i2p.syndie.data;
-
-/**
- *
- */
-public class SafeURL {
-    private String _schema;
-    private String _location;
-    private String _name;
-    private String _description;
-    
-    public SafeURL(String raw) {
-        parse(raw);
-    }
-    
-    private void parse(String raw) {
-        if (raw != null) {
-            int index = raw.indexOf("://");
-            if ( (index <= 0) || (index + 1 >= raw.length()) )
-                return;
-            _schema = raw.substring(0, index);
-            _location = raw.substring(index+3);
-            _location.replace('>', '_');
-            _location.replace('<', '^');
-        }
-    }
-    
-    public String getSchema() { return _schema; }
-    public String getLocation() { return _location; }
-    
-    public String toString() { return _schema + "://" + _location; }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/data/ThreadIndex.java b/apps/syndie/java/src/net/i2p/syndie/data/ThreadIndex.java
deleted file mode 100644
index 810dceb1eb672ce07b1870aeb59b9928b15aaffe..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/data/ThreadIndex.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package net.i2p.syndie.data;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * List of threads, ordered with the most recently updated thread first.
- * Each node in the tree summarizes everything underneath it as well.
- *
- */
-public class ThreadIndex {
-    /** ordered list of threads, with most recent first */
-    private List _roots;
-    /** map of BlogURI to ThreadNode */
-    private Map _nodes;
-    
-    protected ThreadIndex() {
-        // no need to synchronize, since the thread index doesn't change after
-        // its first built
-        _roots = new ArrayList();
-        _nodes = new HashMap(64);
-    }
-    
-    public int getRootCount() { return _roots.size(); }
-    public ThreadNode getRoot(int index) { return (ThreadNode)_roots.get(index); }
-    public ThreadNode getNode(BlogURI uri) { return (ThreadNode)_nodes.get(uri); }
-    /** 
-     * get the root of the thread that the given uri is located in, or -1.
-     * The implementation depends only on getRoot/getNode/getRootCount and not on the
-     * data structures, so should be safe for subclasses who adjust those methods
-     *
-     */
-    public int getRoot(BlogURI uri) {
-        ThreadNode node = getNode(uri);
-        if (node == null) return -1;
-        while (node.getParent() != null)
-            node = node.getParent();
-        for (int i = 0; i < getRootCount(); i++) {
-            ThreadNode cur = getRoot(i);
-            if (cur.equals(node))
-                return i;
-        }
-        return -1;
-    }
-  
-    /** call this in the right order - most recently updated thread first */
-    protected void addRoot(ThreadNode node) { _roots.add(node); }
-    /** invocation order here doesn't matter */
-    protected void addEntry(BlogURI uri, ThreadNode node) { _nodes.put(uri, node); }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/data/ThreadNode.java b/apps/syndie/java/src/net/i2p/syndie/data/ThreadNode.java
deleted file mode 100644
index 437772d712e48fae5a9e8468b0e1c5d08867225d..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/data/ThreadNode.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package net.i2p.syndie.data;
-
-import java.util.Collection;
-import java.util.Iterator;
-
-import net.i2p.data.Hash;
-
-/**
- *
- */
-public interface ThreadNode {
-    /** current post */
-    public BlogURI getEntry();
-    /** how many direct replies there are to the current entry */
-    public int getChildCount();
-    /** the given direct reply */
-    public ThreadNode getChild(int index);
-    /** parent this is actually a reply to */
-    public BlogURI getParentEntry();
-    /** parent in the tree, maybe not a direct parent, but the closest one */
-    public ThreadNode getParent();
-    /** true if this entry, or any child, is written by the given author */
-    public boolean containsAuthor(Hash author);
-    /** true if this node, or any child, includes the given URI */
-    public boolean containsEntry(BlogURI uri);
-    /** list of tags (String) of this node only */
-    public Collection getTags();
-    /** list of tags (String) of this node or any children in the tree */
-    public Collection getRecursiveTags();
-    /** date of the most recent post, recursive */
-    public long getMostRecentPostDate();
-    /** author of the most recent post, recurisve */
-    public Hash getMostRecentPostAuthor();
-    /** walk across the authors of the entire thread */
-    public Iterator getRecursiveAuthorIterator();
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/data/TransparentArchiveIndex.java b/apps/syndie/java/src/net/i2p/syndie/data/TransparentArchiveIndex.java
deleted file mode 100644
index 85d15996d18d4e5529e5e435f757a7c8aa40143f..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/data/TransparentArchiveIndex.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package net.i2p.syndie.data;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-import java.util.Properties;
-import java.util.Set;
-
-import net.i2p.I2PAppContext;
-import net.i2p.data.Hash;
-import net.i2p.syndie.BlogManager;
-
-/**
- * Simple read-only summary of an archive, proxied to the BlogManager's instance
- */
-public class TransparentArchiveIndex extends ArchiveIndex {
-    public TransparentArchiveIndex() { super(I2PAppContext.getGlobalContext(), false); }
-    
-    private static ArchiveIndex index() { return BlogManager.instance().getArchive().getIndex(); }
-    
-    public String getVersion() { return index().getVersion(); }
-    public Properties getHeaders() { return index().getHeaders(); }
-    public int getAllBlogs() { return index().getAllBlogs(); }
-    public int getNewBlogs() { return index().getNewBlogs(); }
-    public int getAllEntries() { return index().getAllEntries(); }
-    public int getNewEntries() { return index().getNewEntries(); }
-    public long getTotalSize() { return index().getTotalSize(); }
-    public long getNewSize() { return index().getNewSize(); }
-    public long getGeneratedOn() { return index().getGeneratedOn(); }
-    public ThreadIndex getThreadedIndex() { return index().getThreadedIndex(); }
-    
-    public String getNewSizeStr() { return index().getNewSizeStr(); }
-    public String getTotalSizeStr() { return index().getTotalSizeStr(); }
-    
-    /** how many blogs/tags are indexed */
-    public int getIndexBlogs() { return index().getIndexBlogs(); }
-    /** get the blog used for the given blog/tag pair */
-    public Hash getBlog(int index) { return index().getBlog(index); }
-    /** get the tag used for the given blog/tag pair */
-    public String getBlogTag(int index) { return index().getBlogTag(index); }
-    /** get the highest entry ID for the given blog/tag pair */
-    public long getBlogLastUpdated(int index) { return index().getBlogLastUpdated(index); }
-    /** get the entry count for the given blog/tag pair */
-    public int getBlogEntryCount(int index) { return index().getBlogEntryCount(index); }
-    /** get the entry from the given blog/tag pair */
-    public BlogURI getBlogEntry(int index, int entryIndex) { return index().getBlogEntry(index, entryIndex); }
-    /** get the raw entry size (including attachments) from the given blog/tag pair */
-    public long getBlogEntrySizeKB(int index, int entryIndex) { return index().getBlogEntrySizeKB(index, entryIndex); }
-    public boolean getEntryIsKnown(BlogURI uri) { return index().getEntryIsKnown(uri); }
-    public long getBlogEntrySizeKB(BlogURI uri) { return index().getBlogEntrySizeKB(uri); }
-    public Set getBlogEntryTags(BlogURI uri) { return index().getBlogEntryTags(uri); }
-    /** how many 'new' blogs are listed */
-    public int getNewestBlogCount() { return index().getNewestBlogCount(); }
-    public Hash getNewestBlog(int index) { return index().getNewestBlog(index); }
-    /** how many 'new' entries are listed */
-    public int getNewestBlogEntryCount() { return index().getNewestBlogEntryCount(); }
-    public BlogURI getNewestBlogEntry(int index) { return index().getNewestBlogEntry(index); }
-    
-    /** list of locally known tags (String) under the given blog */
-    public List getBlogTags(Hash blog) { return index().getBlogTags(blog); }
-    /** list of unique blogs locally known (set of Hash) */
-    public Set getUniqueBlogs() { return index().getUniqueBlogs(); }
-    public void setLocation(String location) { return; }
-    public void setIsLocal(String val) { return; }
-    public void load(File location) throws IOException { return; }
-    /** load up the index from an archive.txt */
-    public void load(InputStream index) throws IOException { return; }
-    
-    /**
-     * Dig through the index for BlogURIs matching the given criteria, ordering the results by
-     * their own entryIds.  
-     *
-     * @param out where to store the matches
-     * @param blog if set, what blog key must the entries be under
-     * @param tag if set, what tag must the entry be in
-     *
-     */
-    public void selectMatchesOrderByEntryId(List out, Hash blog, String tag) { 
-        index().selectMatchesOrderByEntryId(out, blog, tag); 
-    }
-    
-    /** export the index into an archive.txt */
-    public String toString() { return index().toString(); }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/Address.java b/apps/syndie/java/src/net/i2p/syndie/sml/Address.java
deleted file mode 100644
index c8caac56d85d29276b4180aad15c55523f6405e7..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/sml/Address.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package net.i2p.syndie.sml;
-
-import net.i2p.data.DataHelper;
-
-/** contains intermediary rendering state */
-class Address {
-    public String name;
-    public String schema;
-    public String location;
-    public String protocol;
-    public int hashCode() { return -1; }
-    public boolean equals(Object o) {
-        Address a = (Address)o;
-        return DataHelper.eq(schema, a.schema) && DataHelper.eq(location, a.location) && DataHelper.eq(protocol, a.protocol) && DataHelper.eq(name, a.name);
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/ArchiveRef.java b/apps/syndie/java/src/net/i2p/syndie/sml/ArchiveRef.java
deleted file mode 100644
index 1b3df20e4f59353669059191aff8542614a3d30d..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/sml/ArchiveRef.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package net.i2p.syndie.sml;
-
-import net.i2p.data.DataHelper;
-
-/** contains intermediary rendering state */
-class ArchiveRef {
-    public String name;
-    public String description;
-    public String locationSchema;
-    public String location;
-    public int hashCode() { return -1; }
-    public boolean equals(Object o) {
-        ArchiveRef a = (ArchiveRef)o;
-        return DataHelper.eq(name, a.name) && DataHelper.eq(description, a.description) 
-               && DataHelper.eq(locationSchema, a.locationSchema) 
-               && DataHelper.eq(location, a.location);
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/Blog.java b/apps/syndie/java/src/net/i2p/syndie/sml/Blog.java
deleted file mode 100644
index 0eb4c14b0b08b55d38c7b43cc7c641ddde8d0988..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/sml/Blog.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package net.i2p.syndie.sml;
-
-import java.util.List;
-
-import net.i2p.data.DataHelper;
-
-/** contains intermediary rendering state */
-class Blog {
-    public String name;
-    public String hash;
-    public String tag;
-    public long entryId;
-    public List locations;
-    public int hashCode() { return -1; }
-    public boolean equals(Object o) {
-        Blog b = (Blog)o;
-        return DataHelper.eq(hash, b.hash) && DataHelper.eq(tag, b.tag) && DataHelper.eq(name, b.name) 
-               && DataHelper.eq(entryId, b.entryId) && DataHelper.eq(locations, b.locations);
-    }
-}
\ No newline at end of file
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/BlogPostInfoRenderer.java b/apps/syndie/java/src/net/i2p/syndie/sml/BlogPostInfoRenderer.java
deleted file mode 100644
index 98a1a3ea5be854581c2aaa014c41d77ffbb02794..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/sml/BlogPostInfoRenderer.java
+++ /dev/null
@@ -1,466 +0,0 @@
-package net.i2p.syndie.sml;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.StringTokenizer;
-
-import net.i2p.I2PAppContext;
-import net.i2p.client.naming.PetName;
-import net.i2p.client.naming.PetNameDB;
-import net.i2p.data.Base64;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.Attachment;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.syndie.data.SafeURL;
-import net.i2p.syndie.web.AddressesServlet;
-import net.i2p.syndie.web.ViewBlogServlet;
-import net.i2p.util.Log;
-
-/**
- * render the metadata of a post for display in the left nav of the blog view
- * (showing the attachments, etc).  Most of this is just duplicated from the HTMLRenderer
- *
- */
-public class BlogPostInfoRenderer extends EventReceiverImpl {
-    private Log _log;
-    protected SMLParser _parser;
-    protected Writer _out;
-    protected User _user;
-    protected Archive _archive;
-    protected EntryContainer _entry;
-    protected int _lastNewlineAt;
-    protected Map _headers;
-    protected List _addresses;
-    protected List _links;
-    protected List _blogs;
-    protected List _archives;
-    protected StringBuffer _bodyBuffer;
-    
-    public BlogPostInfoRenderer(I2PAppContext ctx) {
-        super(ctx);
-        _log = ctx.logManager().getLog(getClass());
-        _parser = new SMLParser(ctx);
-    }
-
-    public void render(User user, Archive archive, EntryContainer entry, Writer out) throws IOException {
-        if (entry == null)
-            return;
-        render(user, archive, entry, entry.getEntry().getText(), out);
-    }
-    public void render(User user, Archive archive, EntryContainer entry, String rawSML, Writer out) throws IOException {
-        prepare(user, archive, entry, rawSML, out);
-        _out.write(_bodyBuffer.toString());
-    }
-    protected void prepare(User user, Archive archive, EntryContainer entry, String rawSML, Writer out) throws IOException {
-        _user = user;
-        _archive = archive;
-        _entry = entry;
-        _out = out;
-        _headers = new HashMap();
-        _bodyBuffer = new StringBuffer(1024);
-        _addresses = new ArrayList();
-        _links = new ArrayList();
-        _blogs = new ArrayList();
-        _archives = new ArrayList();
-        _parser.parse(rawSML, this);
-    }
-    
-    public void receiveEnd() { 
-        BlogURI postURI = null;
-        Attachment attachments[] = null;
-        if (_entry != null) {
-            attachments = _entry.getAttachments();
-            postURI = _entry.getURI();
-        }
-        renderAttachments(postURI, attachments);
-        renderBlogs(postURI);
-        renderLinks(postURI);
-        renderAddresses(postURI);
-        renderArchives(postURI);
-    }
-    
-    private void renderAttachments(BlogURI postURI, Attachment attachments[]) {
-        renderAttachments(postURI, "syndieBlogPostInfo", attachments, _bodyBuffer);
-    }
-    public static void renderAttachments(BlogURI postURI, String baseStyleName, Attachment attachments[], StringBuffer out) {
-        if ( (attachments != null) && (attachments.length > 0) ) {
-            out.append("<div class=\"").append(baseStyleName).append("Group\">\n");
-            out.append("<span class=\"").append(baseStyleName).append("GroupName\">Attachments</span>\n<ol>");
-            
-            // for each attachment:
-            //   <li><a>$name</a>\n($size of type $type)</li>
-            for (int i = 0; i < attachments.length; i++) {
-                out.append("<li>");
-                String name = attachments[i].getName();
-                if ( (name == null) && (name.trim().length() <= 0) )
-                    name = "Attachment " + i;
-                
-                if (postURI != null) {
-                    out.append("<a href=\"blog.jsp?").append(ViewBlogServlet.PARAM_ATTACHMENT).append("=");
-                    out.append(postURI.getKeyHash().toBase64()).append("/").append(postURI.getEntryId());
-                    out.append("/").append(i).append("\" title=\"View the attachment\">");
-                }
-                out.append(HTMLRenderer.sanitizeString(name, 40));
-                if (postURI != null)
-                    out.append("</a>");
-                
-                out.append("\n(");
-                int bytes = attachments[i].getDataLength();
-                if (bytes > 10*1024*1024)
-                    out.append(bytes/(1024*1024)).append("MBytes");
-                else if (bytes > 10*1024)
-                    out.append(bytes/(10*1024)).append("KBytes");
-                else
-                    out.append(bytes).append("Bytes");
-                
-                String type = attachments[i].getMimeType();
-                if (type != null) {
-                    if ("application/octet-stream".equals(type)) {
-                        out.append(", binary");
-                    } else {
-                        int split = type.lastIndexOf('/');
-                        if (split > 0)
-                            out.append(", ").append(HTMLRenderer.sanitizeString(type.substring(split+1), 30));
-                        else
-                            out.append(", ").append(HTMLRenderer.sanitizeString(type, 30));
-                    }
-                }
-                
-                out.append(")");
-                
-                String desc = attachments[i].getDescription();
-                if ( (desc != null) && (desc.trim().length() > 0) )
-                    out.append("<br />\n").append(HTMLRenderer.sanitizeString(desc, 120));
-                
-                out.append("</li>\n");
-            }
-            out.append("</ol>\n");
-            out.append("</div><!-- syndieBlogPostInfoGroup -->\n");
-        }
-    }
-    
-    private void renderBlogs(BlogURI postURI) {
-        renderBlogs(postURI, _user, "syndieBlogPostInfo", _blogs, _bodyBuffer);
-    }
-    public static void renderBlogs(BlogURI postURI, User user, String baseStyleName, List blogs, StringBuffer out) {
-        if ( (blogs != null) && (blogs.size() > 0) ) {
-            out.append("<div class=\"").append(baseStyleName).append("Group\">\n");
-            out.append("<span class=\"").append(baseStyleName).append("GroupName\">Blogs</span>\n<ol>");
-            
-            // for each blog ref:
-            //   <li><a>$name</a>\n ? :) :(</li>
-            for (int i = 0; i < blogs.size(); i++) {
-                out.append("<li>");
-                Blog blog = (Blog)blogs.get(i);
-                PetNameDB db = user.getPetNameDB();
-                PetName pn = db.getByLocation(blog.hash);
-                
-                if ( (blog.entryId > 0) && (blog.hash != null) ) {
-                    // view a specific post in their blog (jumping to their blog, rather than keeping the
-                    // current blog's formatting... is that the right thing to do?)
-                    out.append("<a href=\"blog.jsp?").append(ViewBlogServlet.PARAM_BLOG).append("=");
-                    out.append(HTMLRenderer.sanitizeTagParam(blog.hash)).append("&amp;");
-                    out.append(ViewBlogServlet.PARAM_ENTRY).append("=").append(HTMLRenderer.sanitizeTagParam(blog.hash));
-                    out.append("/").append(blog.entryId);
-                    out.append("&amp;\" title=\"View the blog post\">");
-                    if (pn != null)
-                        out.append(HTMLRenderer.sanitizeString(pn.getName()));
-                    else
-                        out.append(HTMLRenderer.sanitizeTagParam(blog.name));
-                    out.append(" on ").append(getEntryDate(blog.entryId));
-                    out.append("</a>");
-                } else if (blog.hash != null) {
-                    // view their full blog
-                    out.append("<a href=\"blog.jsp?").append(ViewBlogServlet.PARAM_BLOG);
-                    out.append("=").append(HTMLRenderer.sanitizeString(blog.hash)).append("\" title=\"View their blog\">");
-
-                    if (pn != null) {
-                        // we already have a petname for this user
-                        out.append(pn.getName()).append("</a>");
-                        /* <a href=\"profile.jsp?");
-                        _bodyBuffer.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append("=");
-                        _bodyBuffer.append(HTMLRenderer.sanitizeTagParam(pn.getLocation())).append("\" title=\"View their profile\">");
-                        _bodyBuffer.append("?</a>");
-                         */
-                    } else {
-                        // this name is already in the addressbook with another location, 
-                        // generate a new nym
-                        while ( (pn = db.getByName(blog.name)) != null)
-                            blog.name = blog.name + ".";
-                        out.append(HTMLRenderer.sanitizeTagParam(blog.name)).append("</a>");
-                        /* <a href=\"profile.jsp?");
-                        _bodyBuffer.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append("=");
-                        _bodyBuffer.append(HTMLRenderer.sanitizeTagParam(blog.hash)).append("\" title=\"View their profile\">");
-                        _bodyBuffer.append("?</a>");
-                         */
-                        // should probably add on some inline-bookmarking support, but we'd need requestURL for that
-                    }
-                }
-                out.append("</li>\n");
-            }
-            out.append("</div><!-- end syndieBlogPostInfoGroup -->\n");
-        }
-    }
-       
-    private static final SimpleDateFormat _dateFormat = new SimpleDateFormat("yyyy/MM/dd", Locale.UK);
-    private static final String getEntryDate(long when) {
-        synchronized (_dateFormat) {
-            try {
-                String str = _dateFormat.format(new Date(when));
-                long dayBegin = _dateFormat.parse(str).getTime();
-                return str + " [" + (when - dayBegin) + "]";
-            } catch (ParseException pe) {
-                // wtf
-                return "unknown";
-            }
-        }
-    }
-
-    private void renderLinks(BlogURI postURI) {
-        renderLinks(postURI, _user, "syndieBlogPostInfo", _links, _bodyBuffer);
-    }
-    public static void renderLinks(BlogURI postURI, User user, String baseStyleName, List links, StringBuffer out) {
-        if ( (links != null) && (links.size() > 0) ) {
-            out.append("<div class=\"").append(baseStyleName).append("Group\">\n");
-            out.append("<span class=\"").append(baseStyleName).append("GroupName\">Links</span>\n<ol>");
-            
-            // for each link:
-            //   <li><a>$location</a></li>
-            for (int i = 0; i < links.size(); i++) {
-                out.append("<li>");
-
-                Link l = (Link)links.get(i);
-                String schema = l.schema;
-                out.append("<a href=\"externallink.jsp?");
-                if (l.schema != null)
-                    out.append("schema=").append(HTMLRenderer.sanitizeURL(l.schema)).append("&amp;");
-                if (l.location != null)
-                    out.append("location=").append(HTMLRenderer.sanitizeURL(l.location)).append("&amp;");
-                out.append("\" title=\"Goto ").append(HTMLRenderer.sanitizeTagParam(l.location)).append("\">");
-                out.append(HTMLRenderer.sanitizeString(l.location, 30)).append(" (");
-                out.append(HTMLRenderer.sanitizeString(l.schema, 6)).append(")</a>");
-                
-                out.append("</li>\n");
-            }
-
-            out.append("</div><!-- end syndieBlogPostInfoGroup -->\n");
-        }
-    }
-    
-    private void renderAddresses(BlogURI postURI) {
-        renderAddresses(postURI, _user, "syndieBlogPostInfo", _addresses, _bodyBuffer);
-    }
-    public static void renderAddresses(BlogURI postURI, User user, String baseStyleName, List addresses, StringBuffer out) {
-        if ( (addresses != null) && (addresses.size() > 0) ) {
-            out.append("<div class=\"").append(baseStyleName).append("Group\">\n");
-            out.append("<span class=\"").append(baseStyleName).append("GroupName\">Addresses</span>\n<ol>");
-            
-            // for each address:
-            //   <li><a>$name</a></li>
-            for (int i = 0; i < addresses.size(); i++) {
-                out.append("<li>");
-                Address a = (Address)addresses.get(i);
-                importAddress(a, user);
-                PetName pn = null;
-                if (user != null)
-                    pn = user.getPetNameDB().getByLocation(a.location);
-                if (pn != null) {
-                    out.append(HTMLRenderer.sanitizeString(pn.getName()));
-                } else {
-                    out.append("<a href=\"addresses.jsp?");
-                    if (a.schema != null)
-                        out.append(AddressesServlet.PARAM_NET).append("=").append(HTMLRenderer.sanitizeTagParam(a.schema)).append("&amp;");
-                    if (a.location != null)
-                        out.append(AddressesServlet.PARAM_LOC).append("=").append(HTMLRenderer.sanitizeTagParam(a.location)).append("&amp;");
-                    if (a.name != null)
-                        out.append(AddressesServlet.PARAM_NAME).append("=").append(HTMLRenderer.sanitizeTagParam(a.name)).append("&amp;");
-                    if (a.protocol != null)
-                        out.append(AddressesServlet.PARAM_PROTO).append("=").append(HTMLRenderer.sanitizeTagParam(a.protocol)).append("&amp;");
-                    out.append("\" title=\"Add this address to your addressbook\">").append(HTMLRenderer.sanitizeString(a.name)).append("</a>");
-                }                    
-                out.append("</li>\n");
-            }
-
-            out.append("</div><!-- end syndieBlogPostInfoGroup -->\n");
-        }
-    }
-    
-    public static void importAddress(Address a, User user) {
-        if (user != null && user.getImportAddresses() && !user.getPetNameDB().containsName(a.name)) {
-            PetName pn = new PetName(a.name, a.schema, a.protocol, a.location);
-            user.getPetNameDB().add(pn);
-            try {
-                user.getPetNameDB().store(user.getAddressbookLocation());
-            } catch (IOException ioe) {
-                //ignore
-            }
-        }
-        if (BlogManager.instance().getImportAddresses() 
-                && I2PAppContext.getGlobalContext().namingService().lookup(a.name) == null 
-                && a.schema.equalsIgnoreCase("i2p")) {
-            PetName pn = new PetName(a.name, a.schema, a.protocol, a.location);
-            I2PAppContext.getGlobalContext().petnameDb().add(pn);
-            try {
-                I2PAppContext.getGlobalContext().petnameDb().store();
-            } catch (IOException ioe) {
-                //ignore
-            }
-        }
-    }
-    
-    private void renderArchives(BlogURI postURI) {
-        renderArchives(postURI, _user, "syndieBlogPostInfo", _archives, _bodyBuffer);
-    }
-    public static void renderArchives(BlogURI postURI, User user, String baseStyleName, List archives, StringBuffer out) {
-        if ( (archives != null) && (archives.size() > 0) ) {
-            out.append("<div class=\"").append(baseStyleName).append("Group\">\n");
-            out.append("<span class=\"").append(baseStyleName).append("GroupName\">Archives</span>\n<ol>");
-            
-            // for each archive:
-            //   <li><a>$name</a> :)<br/>$description</li>
-            for (int i = 0; i < archives.size(); i++) {
-                out.append("<li>");
-                ArchiveRef a = (ArchiveRef)archives.get(i);
-                boolean authRemote = BlogManager.instance().authorizeRemote(user);
-                if (authRemote) {
-                    out.append("<a href=\"");
-                    out.append(HTMLRenderer.getArchiveURL(null, new SafeURL(a.locationSchema + "://" + a.location)));
-                    out.append("\" title=\"Browse the remote archive\">");
-                }
-                
-                out.append(HTMLRenderer.sanitizeString(a.name));
-                
-                if (authRemote) {
-                    out.append("</a>");
-                }
-                
-                if ( (a.description != null) && (a.description.trim().length() > 0) )
-                    out.append(" ").append(HTMLRenderer.sanitizeString(a.description, 64));
-                /*
-                _bodyBuffer.append(" <a href=\"").append(HTMLRenderer.getBookmarkURL(a.name, a.location, a.locationSchema, AddressesServlet.PROTO_ARCHIVE));
-                _bodyBuffer.append("\" title=\"Bookmark the remote archive\">bookmark it</a>");
-                */
-                out.append("</li>\n");
-            }
-
-            out.append("</div><!-- end syndieBlogPostInfoGroup -->\n");
-        }        
-    }
-    
-    public void receiveHeader(String header, String value) {
-        //System.err.println("Receive header [" + header + "] = [" + value + "]");
-        if (HTMLRenderer.HEADER_PETNAME.equals(header)) {
-            StringTokenizer tok = new StringTokenizer(value, "\t\n");
-            if (tok.countTokens() != 4)
-                return;
-            String name = tok.nextToken();
-            String net = tok.nextToken();
-            String proto = tok.nextToken();
-            String loc = tok.nextToken();
-            Address a = new Address();
-            a.name = HTMLRenderer.sanitizeString(name, false);
-            a.schema = HTMLRenderer.sanitizeString(net, false);
-            a.protocol = HTMLRenderer.sanitizeString(proto, false);
-            a.location = HTMLRenderer.sanitizeString(loc, false);
-            _addresses.add(a);
-        }
-    }
-    
-    public void receiveHeaderEnd() { }
-
-    public void receivePlain(String text) { }
-    
-    public void receiveBold(String text) { }
-    public void receiveItalic(String text) { }
-    public void receiveUnderline(String text) { }
-    public void receiveHR() { }
-    public void receiveH1(String body) { }
-    public void receiveH2(String body) { }
-    public void receiveH3(String body) { }
-    public void receiveH4(String body) { }
-    public void receiveH5(String body) { }
-    public void receivePre(String body) { }
-    public void receiveQuote(String text, String whoQuoted, String quoteLocationSchema, String quoteLocation) { }
-    public void receiveCode(String text, String codeLocationSchema, String codeLocation) { }
-    public void receiveImage(String alternateText, int attachmentId) { }
-    public void receiveCut(String summaryText) { }
-    public void receiveNewline() { }
-    public void receiveLT() { }
-    public void receiveGT() { }
-    public void receiveBegin() {}
-    public void receiveLeftBracket() { }
-    public void receiveRightBracket() { }
-    
-    /**
-     * when we see a link to a blog, we may want to:
-     * = view the blog entry
-     * = view all entries in that blog
-     * = view all entries in that blog with the given tag
-     * = view the blog's metadata
-     * = [fetch the blog from other locations]
-     * = [add the blog's locations to our list of known locations]
-     * = [shitlist the blog]
-     * = [add the blog to one of our groups]
-     *
-     * [blah] implies *later*.
-     */
-    public void receiveBlog(String name, String hash, String tag, long entryId, List locations, String description) {
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receiving the blog: " + name + "/" + hash + "/" + tag + "/" + entryId +"/" + locations + ": "+ description);
-        byte blogData[] = Base64.decode(hash);
-        if ( (blogData == null) || (blogData.length != Hash.HASH_LENGTH) )
-            return;
-    
-        Blog b = new Blog();
-        b.name = name;
-        b.hash = hash;
-        b.tag = tag;
-        b.entryId = entryId;
-        b.locations = locations;
-        if (!_blogs.contains(b))
-            _blogs.add(b);
-    }
-    
-    public void receiveArchive(String name, String description, String locationSchema, String location, 
-                               String postingKey, String anchorText) {        
-        ArchiveRef a = new ArchiveRef();
-        a.name = name;
-        a.description = description;
-        a.locationSchema = locationSchema;
-        a.location = location;
-        if (!_archives.contains(a))
-            _archives.add(a);
-    }
-    
-    public void receiveLink(String schema, String location, String text) {
-        Link l = new Link();
-        l.schema = schema;
-        l.location = location;
-        if (!_links.contains(l))
-            _links.add(l);
-    }
-
-    public void receiveAddress(String name, String schema, String protocol, String location, String anchorText) {
-        Address a = new Address();
-        a.name = name;
-        a.schema = schema;
-        a.location = location;
-        a.protocol = protocol;
-        if (!_addresses.contains(a))
-            _addresses.add(a);
-    }
-    
-    public void receiveAttachment(int id, int thumb, String anchorText) { }    
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/BlogRenderer.java b/apps/syndie/java/src/net/i2p/syndie/sml/BlogRenderer.java
deleted file mode 100644
index 55b19f5f4c619c1983de3faadb02da272e718059..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/sml/BlogRenderer.java
+++ /dev/null
@@ -1,244 +0,0 @@
-package net.i2p.syndie.sml;
-
-import java.io.IOException;
-import java.io.Writer;
-
-import net.i2p.I2PAppContext;
-import net.i2p.client.naming.PetName;
-import net.i2p.data.Base64;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.Attachment;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogInfoData;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.syndie.data.ThreadNode;
-import net.i2p.syndie.web.PostServlet;
-import net.i2p.syndie.web.ViewBlogServlet;
-
-/**
- * Renders posts for display within the blog view
- *
- */
-public class BlogRenderer extends HTMLRenderer {
-    private BlogInfo _blog;
-    private BlogInfoData _data;
-    private boolean _isComment;
-    public BlogRenderer(I2PAppContext ctx, BlogInfo info, BlogInfoData data) {
-        super(ctx);
-        _blog = info;
-        _data = data;
-        _isComment = false;
-    }
-
-    public void renderPost(User user, Archive archive, EntryContainer entry, Writer out, boolean cutBody, boolean showImages) throws IOException {
-        _isComment = false;
-        render(user, archive, entry, out, cutBody, showImages);
-    }
-    public void renderComment(User user, Archive archive, EntryContainer entry, Writer out) throws IOException {
-        _isComment = true;
-        render(user, archive, entry, out, false, true);
-    }
-    
-    public void receiveHeaderEnd() {
-        _preBodyBuffer.append("<div class=\"syndieBlogPost\" id=\"");
-        _preBodyBuffer.append(_entry.getURI().getKeyHash().toBase64()).append('/').append(_entry.getURI().getEntryId());
-        _preBodyBuffer.append("\"><hr style=\"display: none\" />\n");
-        _preBodyBuffer.append("<div class=\"syndieBlogPostHeader\">\n");
-        _preBodyBuffer.append("<div class=\"syndieBlogPostSubject\">");
-        String subject = (String)_headers.get(HEADER_SUBJECT);
-        if (subject == null)
-            subject = "[no subject]";
-        String tags[] = _entry.getTags();
-        for (int i = 0; (tags != null) && (i < tags.length); i++)
-            displayTag(_preBodyBuffer, _data, tags[i]);
-        _preBodyBuffer.append(getSpan("subjectText")).append(sanitizeString(subject)).append("</span></div>\n");
-        
-        String name = getAuthor();
-        String when = getEntryDate(_entry.getURI().getEntryId());
-        _preBodyBuffer.append("<div class=\"syndieBlogPostFrom\">Posted by: <a href=\"");
-        _preBodyBuffer.append(getMetadataURL(_entry.getURI().getKeyHash()));
-        _preBodyBuffer.append("\" title=\"View their profile\">");
-        _preBodyBuffer.append(sanitizeString(name));
-        _preBodyBuffer.append("</a> on ");
-        _preBodyBuffer.append(when);
-        _preBodyBuffer.append("</div>\n");
-        _preBodyBuffer.append("</div><!-- end syndieBlogPostHeader -->\n");
-        
-        _preBodyBuffer.append("<div class=\"syndieBlogPostSummary\">\n");
-    }
-    
-    public void receiveEnd() { 
-        _postBodyBuffer.append("</div><!-- end syndieBlogPostSummary -->\n");
-        _postBodyBuffer.append("<div class=\"syndieBlogPostDetails\">\n");
-        int childCount = getChildCount(_archive.getIndex().getThreadedIndex().getNode(_entry.getURI()));
-        if ( (_cutReached || childCount > 0) && (_cutBody) ) {
-            _postBodyBuffer.append("<a href=\"");
-            _postBodyBuffer.append(getEntryURL()).append("\" title=\"View comments on this post\">Read more</a> ");
-        }
-        if (childCount > 0) {
-            _postBodyBuffer.append(childCount).append(" ");
-            if (childCount > 1)
-                _postBodyBuffer.append(" comments already, ");
-            else
-                _postBodyBuffer.append(" comment already, ");
-        }
-        _postBodyBuffer.append("<a href=\"");
-        _postBodyBuffer.append(getReplyURL()).append("\" title=\"Reply to this post\">Leave a comment</a>\n");
-        if (_isComment)
-            renderCommentMeta();
-        _postBodyBuffer.append("</div><!-- end syndieBlogPostDetails -->\n");
-        _postBodyBuffer.append("</div><!-- end syndieBlogPost -->\n\n");
-    }
-    
-    private void renderCommentMeta() {
-        BlogURI postURI = null;
-        Attachment attachments[] = null;
-        if (_entry != null) {
-            postURI = _entry.getURI();
-            attachments = _entry.getAttachments();
-        }
-        BlogPostInfoRenderer.renderAttachments(postURI, "syndieBlogCommentInfo", attachments, _postBodyBuffer);
-        BlogPostInfoRenderer.renderBlogs(postURI, _user, "syndieBlogCommentInfo", _blogs, _postBodyBuffer);
-        BlogPostInfoRenderer.renderLinks(postURI, _user, "syndieBlogCommentInfo", _links, _postBodyBuffer);
-        BlogPostInfoRenderer.renderAddresses(postURI, _user, "syndieBlogCommentInfo", _addresses, _postBodyBuffer);
-        BlogPostInfoRenderer.renderArchives(postURI, _user, "syndieBlogCommentInfo", _archives, _postBodyBuffer);
-    }
-    
-    private int getChildCount(ThreadNode node) {
-        int nodes = 0;
-        for (int i = 0; i < node.getChildCount(); i++) {
-            nodes++;
-            nodes += getChildCount(node.getChild(i));
-        }
-        return nodes;
-    }
-    
-    private String getAuthor() {
-        PetName pn = null;
-        if ( (_entry != null) && (_user != null) )
-            pn = _user.getPetNameDB().getByLocation(_entry.getURI().getKeyHash().toBase64());
-        if (pn != null)
-            return pn.getName();
-        BlogInfo info = null;
-        if (_entry != null) {
-            info = _archive.getBlogInfo(_entry.getURI());
-            if (info != null) {
-                String str = info.getProperty(BlogInfo.NAME);
-                if (str != null)
-                    return str;
-            }
-            return _entry.getURI().getKeyHash().toBase64().substring(0,6);
-        } else {
-            return "No name?";
-        }
-    }
-
-    private void displayTag(StringBuffer buf, BlogInfoData data, String tag) {
-        //buf.append("<a href=\"");
-        //buf.append(getPageURL(_blog.getKey().calculateHash(), tag, -1, null, 5, 0, false, true));
-        //buf.append("\" title=\"Filter the blog by the tag '").append(sanitizeTagParam(tag)).append("'\">");
-        if ( (tag == null) || ("[none]".equals(tag) ) )
-            return;
-        buf.append("<img src=\"").append(getTagIconURL(tag)).append("\" alt=\"");
-        buf.append(sanitizeTagParam(tag)).append("\" />");
-        //buf.append("</a>");
-        buf.append(" ");
-    }
-    
-    public String getMetadataURL(Hash blog) { return ThreadedHTMLRenderer.buildProfileURL(blog); }
-    private String getTagIconURL(String tag) {
-        return "viewicon.jsp?tag=" + Base64.encode(tag) + "&amp;" + 
-               ViewBlogServlet.PARAM_BLOG + "=" + _blog.getKey().calculateHash().toBase64();
-    }
-    
-    private String getReplyURL() { 
-        String subject = (String)_headers.get(HEADER_SUBJECT);
-        if (subject != null) {
-            if (!subject.startsWith("re:"))
-                subject = "re: " + subject;
-        } else {
-            subject = "re: ";
-        }
-        return "post.jsp?" + PostServlet.PARAM_PARENT + "=" 
-               + Base64.encode(_entry.getURI().getKeyHash().toBase64() + "/" + _entry.getURI().getEntryId()) + "&amp;"
-               + PostServlet.PARAM_SUBJECT + "=" + sanitizeTagParam(subject) + "&amp;";
-    }
-    
-    protected String getEntryURL() { return getEntryURL(_user != null ? _user.getShowImages() : true); }
-    protected String getEntryURL(boolean showImages) {
-        return getEntryURL(_entry, _blog, showImages);
-    }
-    static String getEntryURL(EntryContainer entry, BlogInfo blog, boolean showImages) { 
-        if (entry == null) return "unknown";
-        return getEntryURL(entry.getURI(), blog, null, showImages); 
-    }
-    static String getEntryURL(BlogURI entry, BlogInfo blog, BlogURI comment, boolean showImages) {
-        if (entry == null) return "unknown";
-        if (comment == null) {
-            return "blog.jsp?" 
-                   + ViewBlogServlet.PARAM_BLOG + "=" + (blog != null ? blog.getKey().calculateHash().toBase64() : "") + "&amp;"
-                   + ViewBlogServlet.PARAM_ENTRY + "="
-                   + Base64.encode(entry.getKeyHash().getData()) + '/' + entry.getEntryId();
-        } else {
-            return "blog.jsp?" 
-                   + ViewBlogServlet.PARAM_BLOG + "=" + (blog != null ? blog.getKey().calculateHash().toBase64() : "") + "&amp;"
-                   + ViewBlogServlet.PARAM_ENTRY + "="
-                   + Base64.encode(entry.getKeyHash().getData()) + '/' + entry.getEntryId()
-                   + '#' + Base64.encode(comment.getKeyHash().getData()) + '/' + comment.getEntryId();
-	}
-    }
-    
-    protected String getAttachmentURLBase() { 
-        return "invalid";
-    }
-    
-    protected String getAttachmentURL(int id) {
-        if (_entry == null) return "unknown";
-        return "blog.jsp?"
-               + ViewBlogServlet.PARAM_BLOG + "=" + _blog.getKey().calculateHash().toBase64() + "&amp;"
-               + ViewBlogServlet.PARAM_ATTACHMENT + "=" 
-               + Base64.encode(_entry.getURI().getKeyHash().getData()) + "/"
-               + _entry.getURI().getEntryId() + "/" 
-               + id;
-    }
-
-    public String getPageURL(String entry) {
-        StringBuffer buf = new StringBuffer(128);
-        buf.append("blog.jsp?");
-        buf.append(ViewBlogServlet.PARAM_BLOG).append(_blog.getKey().calculateHash().toBase64()).append("&amp;");
-
-        if (entry != null) {
-            if (entry.startsWith("entry://"))
-                entry = entry.substring("entry://".length());
-            else if (entry.startsWith("blog://"))
-                entry = entry.substring("blog://".length());
-            int split = entry.indexOf('/');
-            if (split > 0) {
-                buf.append(ViewBlogServlet.PARAM_ENTRY).append("=");
-                buf.append(sanitizeTagParam(entry.substring(0, split))).append('/');
-                buf.append(sanitizeTagParam(entry.substring(split+1))).append("&amp;");
-            }
-        }
-        return buf.toString();
-    }
-    public String getPageURL(Hash blog, String tag, long entryId, String group, int numPerPage, int pageNum, boolean expandEntries, boolean showImages) {
-        StringBuffer buf = new StringBuffer(128);
-        buf.append("blog.jsp?");
-        buf.append(ViewBlogServlet.PARAM_BLOG).append("=");
-        buf.append(_blog.getKey().calculateHash().toBase64()).append("&amp;");
-        
-        if ( (blog != null) && (entryId > 0) ) {
-            buf.append(ViewBlogServlet.PARAM_ENTRY).append("=");
-            buf.append(blog.toBase64()).append('/');
-            buf.append(entryId).append("&amp;");
-        }
-        if (tag != null)
-            buf.append(ViewBlogServlet.PARAM_TAG).append('=').append(sanitizeTagParam(tag)).append("&amp;");
-        if ( (pageNum >= 0) && (numPerPage > 0) )
-            buf.append(ViewBlogServlet.PARAM_OFFSET).append('=').append(pageNum*numPerPage).append("&amp;");
-        return buf.toString();
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/EventReceiverImpl.java b/apps/syndie/java/src/net/i2p/syndie/sml/EventReceiverImpl.java
deleted file mode 100644
index 515f80b86db680d65f536e72e93f842c3a2d4ec1..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/sml/EventReceiverImpl.java
+++ /dev/null
@@ -1,116 +0,0 @@
-package net.i2p.syndie.sml;
-
-import java.util.List;
-
-import net.i2p.I2PAppContext;
-import net.i2p.util.Log;
-
-/**
- *
- */
-public class EventReceiverImpl implements SMLParser.EventReceiver {
-    protected I2PAppContext _context;
-    private Log _log;
-    
-    public EventReceiverImpl(I2PAppContext ctx) {
-        _context = ctx;
-        _log = ctx.logManager().getLog(EventReceiverImpl.class);
-    }
-    public void receiveHeader(String header, String value) {
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive header [" + header + "] = [" + value + "]");
-    }
-    public void receiveLink(String schema, String location, String text) {
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive link [" + schema + "]/[" + location+ "]/[" + text + "]");
-    }
-    public void receiveBlog(String name, String blogKeyHash, String blogPath, long blogEntryId, 
-                            List blogArchiveLocations, String anchorText) {
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive blog [" + name + "]/[" + blogKeyHash + "]/[" + blogPath
-                           + "]/[" + blogEntryId + "]/[" + blogArchiveLocations + "]/[" + anchorText + "]");
-    }
-    public void receiveArchive(String name, String description, String locationSchema, String location, 
-                               String postingKey, String anchorText) {
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive archive [" + name + "]/[" + description + "]/[" + locationSchema 
-                           + "]/[" + location + "]/[" + postingKey + "]/[" + anchorText + "]");
-    }
-    public void receiveImage(String alternateText, int attachmentId) {
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive image [" + alternateText + "]/[" + attachmentId + "]");
-    }
-    public void receiveAddress(String name, String schema, String protocol, String location, String anchorText) {
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive address [" + name + "]/[" + schema + "]/[" + location + "]/[" + anchorText+ "]");
-    }
-    public void receiveBold(String text) { 
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive bold [" + text+ "]"); 
-    }
-    public void receiveItalic(String text) { 
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive italic [" + text+ "]"); 
-    }
-    public void receiveUnderline(String text) { 
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive underline [" + text+ "]"); 
-    }
-    public void receiveQuote(String text, String whoQuoted, String quoteLocationSchema, String quoteLocation) {
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive quote [" + text + "]/[" + whoQuoted + "]/[" + quoteLocationSchema + "]/[" + quoteLocation + "]");
-    }
-    public void receiveCode(String text, String codeLocationSchema, String codeLocation) { 
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive code [" + text+ "]/[" + codeLocationSchema + "]/[" + codeLocation + "]");
-    }
-    public void receiveCut(String summaryText) { 
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive cut [" + summaryText + "]"); 
-    }
-    public void receivePlain(String text) { 
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive plain [" + text + "]"); 
-    }
-    public void receiveNewline() { 
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive NL"); 
-    }
-    public void receiveLT() { 
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive LT"); 
-    }
-    public void receiveGT() { 
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive GT"); 
-    }
-    public void receiveBegin() { 
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive begin"); 
-    }
-    public void receiveEnd() { 
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive end"); 
-    }
-    public void receiveHeaderEnd() { 
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive header end"); 
-    }
-    public void receiveLeftBracket() { 
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive ["); 
-    }
-    public void receiveRightBracket() { 
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receive ]"); 
-    }
-    
-    public void receiveH1(String text) {}
-    public void receiveH2(String text) {}
-    public void receiveH3(String text) {}
-    public void receiveH4(String text) {}
-    public void receiveH5(String text) {}
-    public void receivePre(String text) {}
-    public void receiveHR() {}
-    public void receiveAttachment(int id, int thumbnail, String anchorText) {}
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLPreviewRenderer.java b/apps/syndie/java/src/net/i2p/syndie/sml/HTMLPreviewRenderer.java
deleted file mode 100644
index 60384230483379401da89c49d78044bbfbb6c451..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLPreviewRenderer.java
+++ /dev/null
@@ -1,164 +0,0 @@
-package net.i2p.syndie.sml;
-
-import java.io.File;
-import java.util.List;
-
-import net.i2p.I2PAppContext;
-import net.i2p.client.naming.PetName;
-import net.i2p.data.Base64;
-import net.i2p.data.Hash;
-import net.i2p.syndie.data.SafeURL;
-import net.i2p.syndie.web.AddressesServlet;
-import net.i2p.syndie.web.ArchiveViewerBean;
-
-/**
- *
- */
-public class HTMLPreviewRenderer extends HTMLRenderer {
-    private List _filenames;
-    private List _fileTypes;
-    private List _files;
-    
-    public HTMLPreviewRenderer(I2PAppContext ctx, List filenames, List fileTypes, List files) {
-        super(ctx);
-        _filenames = filenames;
-        _fileTypes = fileTypes;
-        _files = files;
-    }
-    
-    protected String getAttachmentURLBase() { return "viewtempattachment.jsp"; }
-    protected String getAttachmentURL(int id) {
-        return getAttachmentURLBase() + "?" + 
-               ArchiveViewerBean.PARAM_ATTACHMENT + "=" + id;
-    }    
-    
-    public void receiveAttachment(int id, int thumb, String anchorText) {
-        anchorText = sanitizeString(anchorText);
-        if (!continueBody()) { return; }
-        if ( (id < 0) || (_files == null) || (id >= _files.size()) ) {
-            _bodyBuffer.append(anchorText);
-        } else {
-            File f = (File)_files.get(id);
-            String name = (String)_filenames.get(id);
-            String type = (String)_fileTypes.get(id);
-            _bodyBuffer.append("<a ").append(getClass("attachmentView")).append(" href=\"").append(getAttachmentURL(id)).append("\">");
-            if(thumb >= 0) {
-                _bodyBuffer.append("<img src=\"").
-                    append(getAttachmentURL(thumb)).
-                    append("\" alt=\"").append(anchorText).
-                    append("\" title=\"").append(anchorText).
-                    append("\" />");
-            } else {
-                _bodyBuffer.append(anchorText);
-            }
-                    
-            _bodyBuffer.append("</a>");
-            _bodyBuffer.append(getSpan("attachmentSummary")).append(" (");
-            _bodyBuffer.append(getSpan("attachmentSummarySize")).append(f.length()/1024).append("KB</span>, ");
-            _bodyBuffer.append(getSpan("attachmentSummaryName")).append(" \"").append(sanitizeString(name)).append("\"</span>, ");
-            _bodyBuffer.append(getSpan("attachmentSummaryType")).append(sanitizeString(type)).append("</span>)</span>");
-        }
-    }
-    
-    public void receiveEnd() { 
-        _postBodyBuffer.append("</td></tr>\n");
-        _postBodyBuffer.append("<tr ").append(getClass("summDetail")).append(" >\n");
-        _postBodyBuffer.append("<form action=\"").append(getAttachmentURLBase()).append("\">\n");
-        _postBodyBuffer.append("<td colspan=\"2\" valign=\"top\" align=\"left\" ").append(getClass("summDetail")).append("> \n");
-
-        if (_files.size() > 0) {
-            _postBodyBuffer.append(getSpan("summDetailAttachment")).append("Attachments:</span> ");
-            _postBodyBuffer.append("<select ").append(getClass("summDetailAttachmentId")).append(" name=\"").append(ArchiveViewerBean.PARAM_ATTACHMENT).append("\">\n");
-            for (int i = 0; i < _files.size(); i++) {
-                _postBodyBuffer.append("<option value=\"").append(i).append("\">");
-                File f = (File)_files.get(i);
-                String name = (String)_filenames.get(i);
-                String type = (String)_fileTypes.get(i);
-                _postBodyBuffer.append(sanitizeString(name));
-                _postBodyBuffer.append(" (").append(f.length()/1024).append("KB");
-                _postBodyBuffer.append(", type ").append(sanitizeString(type)).append(")</option>\n");
-            }
-            _postBodyBuffer.append("</select>\n");
-            _postBodyBuffer.append("<input ").append(getClass("summDetailAttachmentDl")).append(" type=\"submit\" value=\"Download\" name=\"Download\" /><br />\n");
-        }
-
-        if (_blogs.size() > 0) {
-            _postBodyBuffer.append(getSpan("summDetailBlog")).append("Blog references:</span> ");
-            for (int i = 0; i < _blogs.size(); i++) {
-                Blog b = (Blog)_blogs.get(i);
-                boolean expanded = (_user != null ? _user.getShowExpanded() : false);
-                boolean images = (_user != null ? _user.getShowImages() : false);
-                _postBodyBuffer.append("<a ").append(getClass("summDetailBlogLink")).append(" href=\"");
-                _postBodyBuffer.append(getPageURL(new Hash(Base64.decode(b.hash)), b.tag, b.entryId, -1, -1, expanded, images));
-                _postBodyBuffer.append("\">").append(sanitizeString(b.name)).append("</a> ");
-            }
-            _postBodyBuffer.append("<br />\n");
-        }
-
-        if (_links.size() > 0) {
-            _postBodyBuffer.append(getSpan("summDetailExternal")).append("External links:</span> ");
-            for (int i = 0; i < _links.size(); i++) {
-                Link l = (Link)_links.get(i);
-                String schema = l.schema;
-                _postBodyBuffer.append("<a ");
-                _postBodyBuffer.append(getClass("summDetailExternalLink")).append(" href=\"externallink.jsp?");
-                if (l.schema != null)
-                        _postBodyBuffer.append("schema=").append(sanitizeURL(l.schema)).append('&');
-                if (l.location != null)
-                            _postBodyBuffer.append("location=").append(sanitizeURL(l.location)).append('&');
-                _postBodyBuffer.append("\">").append(sanitizeString(l.location));
-                _postBodyBuffer.append(getSpan("summDetailExternalNet")).append(" (").append(sanitizeString(l.schema)).append(")</span></a> ");
-            }
-            _postBodyBuffer.append("<br />\n");
-        }
-
-        if (_addresses.size() > 0) {
-            _postBodyBuffer.append(getSpan("summDetailAddr")).append("Addresses:</span>");
-            for (int i = 0; i < _addresses.size(); i++) {
-                Address a = (Address)_addresses.get(i);
-                importAddress(a);
-                PetName pn = null;
-                if (_user != null)
-                    pn = _user.getPetNameDB().getByLocation(a.location);
-                if (pn != null) {
-                    _postBodyBuffer.append(' ').append(getSpan("summDetailAddrKnown"));
-                    _postBodyBuffer.append(sanitizeString(pn.getName())).append("</span>");
-                } else {
-                    _postBodyBuffer.append(" <a ").append(getClass("summDetailAddrLink")).append(" href=\"addresses.jsp?");
-                    if (a.schema != null)
-                        _postBodyBuffer.append(AddressesServlet.PARAM_NET).append("=").append(sanitizeTagParam(a.schema)).append('&');
-                    if (a.location != null)
-                        _postBodyBuffer.append(AddressesServlet.PARAM_LOC).append("=").append(sanitizeTagParam(a.location)).append('&');
-                    if (a.name != null)
-                        _postBodyBuffer.append(AddressesServlet.PARAM_NAME).append("=").append(sanitizeTagParam(a.name)).append('&');
-                    if (a.protocol != null)
-                        _postBodyBuffer.append(AddressesServlet.PARAM_PROTO).append("=").append(sanitizeTagParam(a.protocol)).append('&');
-                    _postBodyBuffer.append("\">").append(sanitizeString(a.name)).append("</a>");
-                } 
-            }
-            _postBodyBuffer.append("<br />\n");
-        }
-
-        if (_archives.size() > 0) {
-            _postBodyBuffer.append(getSpan("summDetailArchive")).append("Archives:</span>");
-            for (int i = 0; i < _archives.size(); i++) {
-                ArchiveRef a = (ArchiveRef)_archives.get(i);
-                _postBodyBuffer.append(" <a ").append(getClass("summDetailArchiveLink")).append(" href=\"").append(getArchiveURL(null, new SafeURL(a.locationSchema + "://" + a.location)));
-                _postBodyBuffer.append("\">").append(sanitizeString(a.name)).append("</a>");
-                if (a.description != null)
-                    _postBodyBuffer.append(": ").append(getSpan("summDetailArchiveDesc")).append(sanitizeString(a.description)).append("</span>");
-                if (null == _user.getPetNameDB().getByLocation(a.location)) {
-                    _postBodyBuffer.append(" <a ").append(getClass("summDetailArchiveBookmark")).append(" href=\"");
-                    _postBodyBuffer.append(getBookmarkURL(a.name, a.location, a.locationSchema, "syndiearchive"));
-                    _postBodyBuffer.append("\">bookmark</a>");
-                }
-            }
-            _postBodyBuffer.append("<br />\n");
-        }
-
-        _postBodyBuffer.append("</td>\n</form>\n</tr>\n");
-        _postBodyBuffer.append("</table>\n");
-    }
-    
-    protected void renderMetaCell() { _preBodyBuffer.append("<td></td></tr>"); }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java b/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java
deleted file mode 100644
index 3c0346ab925f912789bd82ff361c4a2f1e3fbd4e..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java
+++ /dev/null
@@ -1,1076 +0,0 @@
-package net.i2p.syndie.sml;
-
-import java.io.ByteArrayOutputStream;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.StringTokenizer;
-
-import net.i2p.I2PAppContext;
-import net.i2p.client.naming.PetName;
-import net.i2p.data.Base64;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.Attachment;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.syndie.data.SafeURL;
-import net.i2p.syndie.web.AddressesServlet;
-import net.i2p.syndie.web.ArchiveViewerBean;
-import net.i2p.syndie.web.PostServlet;
-import net.i2p.syndie.web.SyndicateServlet;
-import net.i2p.util.Log;
-
-/**
- *
- */
-public class HTMLRenderer extends EventReceiverImpl {
-    private Log _log;
-    protected SMLParser _parser;
-    protected Writer _out;
-    protected User _user;
-    protected Archive _archive;
-    protected EntryContainer _entry;
-    protected boolean _showImages;
-    protected boolean _cutBody;
-    protected boolean _cutReached;
-    protected int _cutSize;
-    protected int _lastNewlineAt;
-    protected Map _headers;
-    protected List _addresses;
-    protected List _links;
-    protected List _blogs;
-    protected List _archives;
-    protected StringBuffer _preBodyBuffer;
-    protected StringBuffer _bodyBuffer;
-    protected StringBuffer _postBodyBuffer;
-    
-    public HTMLRenderer(I2PAppContext ctx) {
-        super(ctx);
-        _log = ctx.logManager().getLog(HTMLRenderer.class);
-        _parser = new SMLParser(ctx);
-    }
-
-    /**
-     * Usage: HTMLRenderer smlFile outputFile
-     */
-    public static void main(String args[]) {
-        if (args.length != 2) {
-            System.err.println("Usage: HTMLRenderer smlFile outputFile");
-            return;
-        }
-        HTMLRenderer renderer = new HTMLRenderer(I2PAppContext.getGlobalContext());
-        Writer out = null;
-        try {
-            ByteArrayOutputStream baos = new ByteArrayOutputStream(1024*512);
-            FileInputStream in = new FileInputStream(args[0]);
-            byte buf[] = new byte[1024];
-            int read = 0;
-            while ( (read = in.read(buf)) != -1)
-                baos.write(buf, 0, read);
-            out = new OutputStreamWriter(new FileOutputStream(args[1]), "UTF-8");
-            renderer.render(new User(), BlogManager.instance().getArchive(), null, DataHelper.getUTF8(baos.toByteArray()), out, false, true);   
-        } catch (IOException ioe) {
-            ioe.printStackTrace();
-        } finally {
-            if (out != null) try { out.close(); } catch (IOException ioe) {}
-        }
-    }
-    
-    /**
-     * Retrieve: class="s_summary_$element" or class="s_detail_$element ss_$style_detail_$element"
-     */
-    protected String getClass(String element) {
-        StringBuffer rv = new StringBuffer(64);
-        rv.append(" class=\"s_");
-        if (_cutBody)
-            rv.append("summary_");
-        else
-            rv.append("detail_");
-        rv.append(element);
-        if (_entry != null) {
-            String style = sanitizeStyle(_entry.getHeader(HEADER_STYLE));
-            if (style != null) {
-                rv.append(" ss_").append(style);
-                if (_cutBody)
-                    rv.append("summary_");
-                else
-                    rv.append("detail_");
-                rv.append(element);
-            }
-        }
-        rv.append("\" ");
-        return rv.toString();
-    }
-    protected String getSpan(String element) {
-        return "<span " + getClass(element) + ">";
-    }
-    
-    public void renderUnknownEntry(User user, Archive archive, BlogURI uri, Writer out) throws IOException {
-        BlogInfo info = archive.getBlogInfo(uri);
-        if (info == null)
-            out.write("<br /><span " + getClass("unknownBlog") + ">The blog <span " + getClass("blogURI") + ">" + uri.getKeyHash().toBase64() + "</span> is not known locally.  "
-                      + "Please get it from an archive and <a " + getClass("unknownRetry") + " href=\"" 
-                      + getPageURL(uri.getKeyHash(), null, uri.getEntryId(), -1, -1, user.getShowExpanded(), user.getShowImages())
-                      + "\">try again</a></span>");
-        else
-            out.write("<br /><span " + getClass("unknownEntry") + ">The blog <a " + getClass("unknownRetry") + " href=\""
-                      + getPageURL(uri.getKeyHash(), null, -1, -1, -1, user.getShowExpanded(), user.getShowImages())
-                      + "\">" + info.getProperty(BlogInfo.NAME) + "</a> is known, but the entry " + uri.getEntryId() + " is not.  "
-                      + "Please get it from an archive and <a " + getClass("unknownRetry") + " href=\"" 
-                      + getPageURL(uri.getKeyHash(), null, uri.getEntryId(), -1, -1, user.getShowExpanded(), user.getShowImages())
-                      + "\">try again</a></span>");
-    }
-    
-    public void render(User user, Archive archive, EntryContainer entry, Writer out, boolean cutBody, boolean showImages) throws IOException {
-        if (entry == null)
-            return;
-        render(user, archive, entry, entry.getEntry().getText(), out, cutBody, showImages);
-    }
-    public void render(User user, Archive archive, EntryContainer entry, String rawSML, Writer out, boolean cutBody, boolean showImages) throws IOException {
-        prepare(user, archive, entry, rawSML, out, cutBody, showImages);
-        
-        _out.write(_preBodyBuffer.toString());
-        _out.write(_bodyBuffer.toString());
-        _out.write(_postBodyBuffer.toString());
-        //int len = _preBodyBuffer.length() + _bodyBuffer.length() + _postBodyBuffer.length();
-        //System.out.println("Wrote " + len);
-    }
-    protected void prepare(User user, Archive archive, EntryContainer entry, String rawSML, Writer out, boolean cutBody, boolean showImages) throws IOException {
-        _user = user;
-        _archive = archive;
-        _entry = entry;
-        _out = out;
-        _headers = new HashMap();
-        _preBodyBuffer = new StringBuffer(1024);
-        _bodyBuffer = new StringBuffer(1024);
-        _postBodyBuffer = new StringBuffer(1024);
-        _addresses = new ArrayList();
-        _links = new ArrayList();
-        _blogs = new ArrayList();
-        _archives = new ArrayList();
-        _cutBody = cutBody;
-        _showImages = showImages;
-        _cutReached = false;
-        _cutSize = 1024;
-        _parser.parse(rawSML, this);
-    }
-    
-    public void receivePlain(String text) { 
-        if (!continueBody()) { return; }
-        if (_log.shouldLog(Log.DEBUG)) _log.debug("receive plain [" + text + "]");
-        _bodyBuffer.append(sanitizeString(text)); 
-    }
-    
-    public void receiveBold(String text) { 
-        if (!continueBody()) { return; }
-        _bodyBuffer.append("<em ").append(getClass("bold")).append(" >").append(sanitizeString(text)).append("</em>");
-    }
-    public void receiveItalic(String text) { 
-        if (!continueBody()) { return; }
-        _bodyBuffer.append("<em ").append(getClass("italic")).append(" >").append(sanitizeString(text)).append("</em>");
-    }
-    public void receiveUnderline(String text) { 
-        if (!continueBody()) { return; }
-        _bodyBuffer.append("<em ").append(getClass("underline")).append(" >").append(sanitizeString(text)).append("</em>");
-    }
-    public void receiveHR() {
-        if (!continueBody()) { return; }
-        if (_log.shouldLog(Log.DEBUG)) _log.debug("receive HR");
-        _bodyBuffer.append(getSpan("hr")).append("<hr /></span>");
-    }
-    public void receiveH1(String body) {
-        if (!continueBody()) { return; }
-        _bodyBuffer.append("<h1 ").append(getClass("h1")).append(" >").append(sanitizeString(body)).append("</span></h1>");
-    }
-    public void receiveH2(String body) {
-        if (!continueBody()) { return; }
-        _bodyBuffer.append("<h2 ").append(getClass("h2")).append(" >").append(sanitizeString(body)).append("</span></h2>");
-    }
-    public void receiveH3(String body) {
-        if (!continueBody()) { return; }
-        _bodyBuffer.append("<h3 ").append(getClass("h3")).append(" >").append(sanitizeString(body)).append("</span></h3>");
-    }
-    public void receiveH4(String body) {
-        if (!continueBody()) { return; }
-        _bodyBuffer.append("<h4 ").append(getClass("h4")).append(" >").append(sanitizeString(body)).append("</span></h4>");
-    }
-    public void receiveH5(String body) {
-        if (!continueBody()) { return; }
-        _bodyBuffer.append("<h5 ").append(getClass("h5")).append(" >").append(sanitizeString(body)).append("</span></h5>");
-    }
-    public void receivePre(String body) {
-        if (!continueBody()) { return; }
-        if (_log.shouldLog(Log.DEBUG)) _log.debug("receive pre: [" + sanitizeString(body) + "]");
-        _bodyBuffer.append("<pre ").append(getClass("pre")).append(" >").append(sanitizeString(body)).append("</pre>");
-    }
-    
-    public void receiveQuote(String text, String whoQuoted, String quoteLocationSchema, String quoteLocation) {
-        if (!continueBody()) { return; }
-        _bodyBuffer.append("<quote ").append(getClass("quote")).append(" >").append(sanitizeString(text)).append("</quote>");
-    }
-    public void receiveCode(String text, String codeLocationSchema, String codeLocation) { 
-        if (!continueBody()) { return; }
-           _bodyBuffer.append("<code ").append(getClass("code")).append(" >").append(sanitizeString(text)).append("</code>");
-    }
-    public void receiveImage(String alternateText, int attachmentId) {
-        if (!continueBody()) { return; }
-        if (_showImages) {
-            _bodyBuffer.append("<img ").append(getClass("img")).append(" src=\"").append(getAttachmentURL(attachmentId)).append("\"");
-            if (alternateText != null)
-                _bodyBuffer.append(" alt=\"").append(sanitizeTagParam(alternateText)).append("\"");
-            _bodyBuffer.append(" />");
-        } else {
-            _bodyBuffer.append(getSpan("imgSummary")).append("[image: ").append(getSpan("imgSummaryAttachment")).append(" attachment ").append(attachmentId);
-            _bodyBuffer.append(":</span> ").append(getSpan("imgSummaryAlt")).append(sanitizeString(alternateText));
-            _bodyBuffer.append("</span> <a ").append(getClass("imgSummaryLink")).append(" href=\"").append(getEntryURL(true)).append("\">view images</a>]</span>");
-        }
-    }
-    
-    public void receiveCut(String summaryText) { 
-        if (!continueBody()) { return; }
-        _cutReached = true;
-        if (_cutBody) {
-            _bodyBuffer.append("<a ").append(getClass("cutExplicit")).append(" href=\"").append(getEntryURL()).append("\">");
-            if ( (summaryText != null) && (summaryText.length() > 0) )
-                _bodyBuffer.append(sanitizeString(summaryText));
-            else
-                _bodyBuffer.append("more inside...");
-            _bodyBuffer.append("</a>\n");
-        } else {
-            if (summaryText != null)
-                _bodyBuffer.append(getSpan("cutIgnore")).append(sanitizeString(summaryText)).append("</span>\n");
-        }
-    }
-    
-    /** are we either before the cut or rendering without cutting? */
-    protected boolean continueBody() {
-        boolean rv = ( (!_cutReached) && (_bodyBuffer.length() <= _cutSize) ) || (!_cutBody);
-        //if (!rv) 
-        //    System.out.println("rv: " + rv + " Cut reached: " + _cutReached + " bodyBufferSize: " + _bodyBuffer.length() + " cutBody? " + _cutBody);
-        if (!rv && !_cutReached) {
-            // exceeded the allowed size
-            _bodyBuffer.append("<a ").append(getClass("cutImplicit")).append(" href=\"").append(getEntryURL()).append("\">more inside...</a>\n");
-            _cutReached = true;
-        }
-        return rv;
-    }
-    
-    public void receiveNewline() { 
-        if (!continueBody()) { return; }
-        if (_log.shouldLog(Log.DEBUG)) _log.debug("receive NL");
-        if (true || (_lastNewlineAt >= _bodyBuffer.length()))
-            _bodyBuffer.append(getSpan("nl")).append("<br /></span>\n");
-        else
-            _lastNewlineAt = _bodyBuffer.length();
-    }
-    public void receiveLT() { 
-        if (!continueBody()) { return; }
-        _bodyBuffer.append(getSpan("lt")).append("&lt;</span>");
-    }
-    public void receiveGT() { 
-        if (!continueBody()) { return; }
-        _bodyBuffer.append(getSpan("gt")).append("&gt;</span>");
-    }
-    public void receiveBegin() {}
-    public void receiveLeftBracket() { 
-        if (!continueBody()) { return; }
-        if (_log.shouldLog(Log.DEBUG)) _log.debug("receive [");
-        _bodyBuffer.append(getSpan("lb")).append("[</span>");
-    }
-    public void receiveRightBracket() { 
-        if (!continueBody()) { return; }
-        if (_log.shouldLog(Log.DEBUG)) _log.debug("receive ]");
-        _bodyBuffer.append(getSpan("rb")).append("]</span>");
-    }
-    
-    /**
-     * when we see a link to a blog, we may want to:
-     * = view the blog entry
-     * = view all entries in that blog
-     * = view all entries in that blog with the given tag
-     * = view the blog's metadata
-     * = [fetch the blog from other locations]
-     * = [add the blog's locations to our list of known locations]
-     * = [shitlist the blog]
-     * = [add the blog to one of our groups]
-     *
-     * [blah] implies *later*.
-     *
-     * Currently renders to:
-     *  <a href="$entryURL">$description</a> 
-     *   [blog: <a href="$blogURL">$name</a> (<a href="$metaURL">meta</a>) 
-     *   [tag: <a href="$blogTagURL">$tag</a>] 
-     *   archived at $location*]
-     *
-     */
-    public void receiveBlog(String name, String hash, String tag, long entryId, List locations, String description) {
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Receiving the blog: " + name + "/" + hash + "/" + tag + "/" + entryId +"/" + locations + ": "+ description);
-        byte blogData[] = Base64.decode(hash);
-        if ( (blogData == null) || (blogData.length != Hash.HASH_LENGTH) )
-            return;
-    
-        Blog b = new Blog();
-        b.name = name;
-        b.hash = hash;
-        b.tag = tag;
-        b.entryId = entryId;
-        b.locations = locations;
-        if (!_blogs.contains(b))
-            _blogs.add(b);
-    
-        if (!continueBody()) { return; }
-        if (hash == null) return;
-        
-        Hash blog = new Hash(blogData);
-        if (entryId > 0) {
-            String pageURL = getPageURL(blog, tag, entryId, -1, -1, true, (_user != null ? _user.getShowImages() : false));
-            _bodyBuffer.append("<a ").append(getClass("blogEntryLink")).append(" href=\"").append(pageURL).append("\">");
-            if ( (description != null) && (description.trim().length() > 0) ) {
-                _bodyBuffer.append(sanitizeString(description));
-            } else if ( (name != null) && (name.trim().length() > 0) ) {
-                _bodyBuffer.append(sanitizeTagParam(name));
-            } else {
-                _bodyBuffer.append("[view entry]");
-            }
-            _bodyBuffer.append("</a>");
-        } else if ( (description != null) && (description.trim().length() > 0) ) {
-            _bodyBuffer.append(sanitizeString(description));
-        }
-        
-        //String url = getPageURL(blog, null, -1, -1, -1, (_user != null ? _user.getShowExpanded() : false), (_user != null ? _user.getShowImages() : false));
-        String url = getMetadataURL(blog);
-        _bodyBuffer.append(getSpan("blogEntrySummary"));
-        _bodyBuffer.append(" [<a ").append(getClass("blogLink")).append(" href=\"").append(url).append("\">");
-        if ( (name != null) && (name.trim().length() > 0) )
-            _bodyBuffer.append(sanitizeTagParam(name));
-        else
-            _bodyBuffer.append("view");
-        _bodyBuffer.append("</a>");
-        //_bodyBuffer.append("</a> (<a ").append(getClass("blogMeta")).append(" href=\"").append(getMetadataURL(blog)).append("\">meta</a>)");
-        if ( (tag != null) && (tag.trim().length() > 0) ) {
-            url = getPageURL(blog, tag, -1, -1, -1, false, false);
-            _bodyBuffer.append(" <a ").append(getClass("blogTagLink")).append(" href=\"").append(url);
-            _bodyBuffer.append("\">Tag: ").append(sanitizeString(tag)).append("</a>");
-        }
-        if ( (locations != null) && (locations.size() > 0) ) {
-            _bodyBuffer.append(getSpan("blogArchive")).append(" Archives: ");
-            for (int i = 0; i < locations.size(); i++) {
-                SafeURL surl = (SafeURL)locations.get(i);
-                if (_user.getAuthenticated() && BlogManager.instance().authorizeRemote(_user) )
-                    _bodyBuffer.append(" <a ").append(getClass("blogArchiveView")).append(" href=\"").append(getArchiveURL(blog, surl)).append("\">").append(sanitizeString(surl.toString())).append("</a> ");
-                else
-                    _bodyBuffer.append(getSpan("blogArchiveURL")).append(sanitizeString(surl.toString())).append("</span> ");
-            }
-            _bodyBuffer.append("</span>");
-        }
-        _bodyBuffer.append("]</span> ");
-    }
-    
-    public void receiveArchive(String name, String description, String locationSchema, String location, 
-                               String postingKey, String anchorText) {        
-        ArchiveRef a = new ArchiveRef();
-        a.name = name;
-        a.description = description;
-        a.locationSchema = locationSchema;
-        a.location = location;
-        if (!_archives.contains(a))
-            _archives.add(a);
-    
-        if (!continueBody()) { return; }
-        
-        _bodyBuffer.append(getSpan("archive")).append(sanitizeString(anchorText)).append("</span>");
-        _bodyBuffer.append(getSpan("archiveSummary")).append(" [Archive ");
-        if (name != null)
-            _bodyBuffer.append(getSpan("archiveSummaryName")).append(sanitizeString(name)).append("</span>");
-        if (location != null) {
-            _bodyBuffer.append(" at ");
-            SafeURL surl = new SafeURL(locationSchema + "://" + location);
-            if (BlogManager.instance().authorizeRemote(_user)) {
-                _bodyBuffer.append("<a ").append(getClass("archiveSummaryLink")).append(" href=\"").append(getArchiveURL(null, surl));
-                _bodyBuffer.append("\">").append(sanitizeString(surl.toString())).append("</a>");
-            } else {
-                _bodyBuffer.append(sanitizeString(surl.getLocation()));
-            }
-            if (_user.getAuthenticated()) {
-                _bodyBuffer.append(" <a ").append(getClass("archiveBookmarkLink")).append(" href=\"");
-                _bodyBuffer.append(getBookmarkURL(sanitizeString(name), surl.getLocation(), surl.getSchema(), AddressesServlet.PROTO_ARCHIVE));
-                _bodyBuffer.append("\">bookmark it</a>");
-            }
-        }
-        if (description != null)
-            _bodyBuffer.append(": ").append(getSpan("archiveSummaryDesc")).append(sanitizeString(description)).append("</span>");
-        _bodyBuffer.append("]</span>");
-    }
-    
-    public void receiveLink(String schema, String location, String text) {
-        Link l = new Link();
-        l.schema = schema;
-        l.location = location;
-        if (!_links.contains(l))
-            _links.add(l);
-        if (!continueBody()) { return; }
-        if ( (schema == null) || (location == null) ) return;
-        _bodyBuffer.append("<a ");
-        _bodyBuffer.append(getClass("externalLink")).append(" href=\"externallink.jsp?schema=");
-        _bodyBuffer.append(sanitizeURL(schema)).append("&location=");
-        _bodyBuffer.append(sanitizeURL(location)).append("&description=");
-        _bodyBuffer.append(sanitizeURL(text));
-        _bodyBuffer.append("\" title=\"Goto ").append(sanitizeTagParam(location));
-        _bodyBuffer.append("\">").
-                    append(sanitizeString(text)).
-                    append("</a>");
-    }
-
-    public void importAddress(Address a) {
-        BlogPostInfoRenderer.importAddress(a, _user);
-    }
-    
-    public void receiveAddress(String name, String schema, String protocol, String location, String anchorText) {
-        Address a = new Address();
-        a.name = name;
-        a.schema = schema;
-        a.location = location;
-        a.protocol = protocol;
-        if (!_addresses.contains(a))
-            _addresses.add(a);
-        if (!continueBody()) { return; }
-        if ( (schema == null) || (location == null) ) return;
-        PetName pn = null;
-        if (_user != null)
-            pn = _user.getPetNameDB().getByLocation(location);
-        if (pn != null) {
-            _bodyBuffer.append(getSpan("addr")).append(sanitizeString(anchorText)).append("</span>");
-            _bodyBuffer.append(getSpan("addrKnownName")).append("(").append(sanitizeString(pn.getName())).append(")</span>");
-        } else {
-            if (_log.shouldLog(Log.DEBUG))
-                _log.debug("Receiving address [" + location + "]");
-            _bodyBuffer.append("<a ").append(getClass("addrAdd")).append(" href=\"addresses.jsp?");
-            if (schema != null)
-                _bodyBuffer.append(AddressesServlet.PARAM_NET).append('=').append(sanitizeTagParam(schema)).append('&');
-            if (name != null)
-                _bodyBuffer.append(AddressesServlet.PARAM_NAME).append('=').append(sanitizeTagParam(name)).append('&');
-            if (protocol != null)
-                _bodyBuffer.append(AddressesServlet.PARAM_PROTO).append('=').append(sanitizeTagParam(protocol)).append('&');
-            if (location != null)
-                _bodyBuffer.append(AddressesServlet.PARAM_LOC).append('=').append(sanitizeTagParam(location));
-            _bodyBuffer.append("\">").append(sanitizeString(anchorText)).append("</a>");
-        }
-    }
-    
-    public void receiveAttachment(int id, int thumb, String anchorText) {
-        if (!continueBody()) { return; }
-        Attachment attachments[] = _entry.getAttachments();
-        if ( (id < 0) || (id >= attachments.length)) {
-            _bodyBuffer.append(getSpan("attachmentUnknown")).append(sanitizeString(anchorText)).append("</span>");
-        } else {
-            _bodyBuffer.append("<a ").append(getClass("attachmentView")).append(" href=\"").append(getAttachmentURL(id)).append("\">");            
-            if(thumb >= 0) {
-                _bodyBuffer.append("<img src=\"").
-                    append(getAttachmentURL(thumb)).
-                    append("\" alt=\"").append(anchorText).
-                    append("\" title=\"").append(anchorText).
-                    append("\" />");
-            } else {
-                _bodyBuffer.append(anchorText);
-            }
-            _bodyBuffer.append("</a>");
-            _bodyBuffer.append(getSpan("attachmentSummary")).append(" (");
-            _bodyBuffer.append(getSpan("attachmentSummarySize")).append(attachments[id].getDataLength()/1024).append("KB</span>, ");
-            _bodyBuffer.append(getSpan("attachmentSummaryName")).append(" \"").append(sanitizeString(attachments[id].getName())).append("\"</span>, ");
-            String descr = attachments[id].getDescription();
-            if ( (descr != null) && (descr.trim().length() > 0) ) {
-                _bodyBuffer.append(getSpan("attachmentSummaryDesc")).append(" \"").append(sanitizeString(descr.trim())).append("\"</span>, ");
-            }
-            _bodyBuffer.append(getSpan("attachmentSummaryType")).append(sanitizeString(attachments[id].getMimeType())).append("</span>)</span>");
-        }
-    }
-    
-    public void receiveEnd() { 
-        _postBodyBuffer.append("</td></tr>\n<!-- end of the post body -->");
-        if (_cutBody) {
-            _postBodyBuffer.append("<!-- beginning of the post summary -->\n");
-            _postBodyBuffer.append("<tr ").append(getClass("summ")).append(">\n");
-            _postBodyBuffer.append("<td colspan=\"2\" valign=\"top\" align=\"left\" ").append(getClass("summ")).append(" >");
-            _postBodyBuffer.append("<a ").append(getClass("summLink")).append(" href=\"").append(getEntryURL()).append("\">View details...</a> ");
-            _postBodyBuffer.append(getSpan("summ"));
-            if ( (_entry != null) && (_entry.getAttachments() != null) && (_entry.getAttachments().length > 0) ) {
-                int num = _entry.getAttachments().length;
-                if (num == 1)
-                    _postBodyBuffer.append("1 attachment ");
-                else
-                    _postBodyBuffer.append(num + " attachments ");
-            }
-            
-            int blogs = _blogs.size();
-            if (blogs == 1)
-                _postBodyBuffer.append("1 blog reference ");
-            else if (blogs > 1)
-                _postBodyBuffer.append(blogs).append(" blog references ");
-            
-            int links = _links.size();
-            if (links == 1)
-                _postBodyBuffer.append("1 external link ");
-            else if (links > 1)
-                _postBodyBuffer.append(links).append(" external links ");
-
-            int addrs = _addresses.size();
-            if (addrs == 1)
-                _postBodyBuffer.append("1 address ");
-            else if (addrs > 1)
-                _postBodyBuffer.append(addrs).append(" addresses ");
-            
-            int archives = _archives.size();
-            if (archives == 1)
-                _postBodyBuffer.append("1 archive ");
-            else if (archives > 1)
-                _postBodyBuffer.append(archives).append(" archives ");
-            
-            if (_entry != null) {
-                List replies = _archive.getIndex().getReplies(_entry.getURI());
-                if ( (replies != null) && (replies.size() > 0) ) {
-                    if (replies.size() == 1)
-                        _postBodyBuffer.append("1 reply ");
-                    else
-                        _postBodyBuffer.append(replies.size()).append(" replies ");
-                }
-            }
-        
-            String inReplyTo = (String)_headers.get(HEADER_IN_REPLY_TO);
-            if ( (inReplyTo != null) && (inReplyTo.trim().length() > 0) )
-                _postBodyBuffer.append(" <a ").append(getClass("summParent")).append(" href=\"threads.jsp?").append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(sanitizeTagParam(inReplyTo)).append("\">(view parent)</a>\n");
-            
-            _postBodyBuffer.append("</span></td></tr>\n");
-            _postBodyBuffer.append("<!-- end of the post summary -->\n");
-        } else {
-            _postBodyBuffer.append("<!-- beginning of the post summary details -->\n");
-            _postBodyBuffer.append("<tr ").append(getClass("summDetail")).append(">\n");
-            _postBodyBuffer.append("<form action=\"").append(getAttachmentURLBase()).append("\">\n");
-            _postBodyBuffer.append("<input type=\"hidden\" name=\"").append(ArchiveViewerBean.PARAM_BLOG);
-            _postBodyBuffer.append("\" value=\"");
-            if (_entry != null)
-                _postBodyBuffer.append(Base64.encode(_entry.getURI().getKeyHash().getData()));
-            else
-                _postBodyBuffer.append("unknown");
-            _postBodyBuffer.append("\" />\n");
-            _postBodyBuffer.append("<input type=\"hidden\" name=\"").append(ArchiveViewerBean.PARAM_ENTRY);
-            _postBodyBuffer.append("\" value=\"");
-            if (_entry != null) 
-                _postBodyBuffer.append(_entry.getURI().getEntryId());
-            else
-                _postBodyBuffer.append("unknown");
-            _postBodyBuffer.append("\" />\n");
-            _postBodyBuffer.append("<td colspan=\"2\" valign=\"top\" align=\"left\" ").append(getClass("summDetail")).append(" >\n");
-
-            if ( (_entry != null) && (_entry.getAttachments() != null) && (_entry.getAttachments().length > 0) ) {
-                _postBodyBuffer.append(getSpan("summDetailAttachment")).append("Attachments:</span> ");
-                _postBodyBuffer.append("<select ").append(getClass("summDetailAttachmentId")).append(" name=\"").append(ArchiveViewerBean.PARAM_ATTACHMENT).append("\">\n");
-                for (int i = 0; i < _entry.getAttachments().length; i++) {
-                    _postBodyBuffer.append("<option value=\"").append(i).append("\">");
-                    Attachment a = _entry.getAttachments()[i];
-                    _postBodyBuffer.append(sanitizeString(a.getName()));
-                    if ( (a.getDescription() != null) && (a.getDescription().trim().length() > 0) ) {
-                        _postBodyBuffer.append(": ");
-                        _postBodyBuffer.append(sanitizeString(a.getDescription()));
-                    }
-                    _postBodyBuffer.append(" (").append(a.getDataLength()/1024).append("KB");
-                    _postBodyBuffer.append(", type ").append(sanitizeString(a.getMimeType())).append(")</option>\n");
-                }
-                _postBodyBuffer.append("</select>\n");
-                _postBodyBuffer.append("<input ").append(getClass("summDetailAttachmentDl")).append(" type=\"submit\" value=\"Download\" name=\"Download\" /><br />\n");
-            }
-
-            if (_blogs.size() > 0) {
-                _postBodyBuffer.append(getSpan("summDetailBlog")).append("Blog references:</span>");
-                for (int i = 0; i < _blogs.size(); i++) {
-                    Blog b = (Blog)_blogs.get(i);
-                    _postBodyBuffer.append("<a ").append(getClass("summDetailBlogLink")).append(" href=\"");
-                    boolean expanded = (_user != null ? _user.getShowExpanded() : false);
-                    boolean images = (_user != null ? _user.getShowImages() : false);
-                    _postBodyBuffer.append(getPageURL(new Hash(Base64.decode(b.hash)), b.tag, b.entryId, -1, -1, expanded, images));
-                    _postBodyBuffer.append("\">").append(sanitizeString(b.name)).append("</a> ");
-                }
-                _postBodyBuffer.append("<br />\n");
-            }
-
-            if (_links.size() > 0) {
-                _postBodyBuffer.append(getSpan("summDetailExternal")).append("External links:</span> ");
-                for (int i = 0; i < _links.size(); i++) {
-                    Link l = (Link)_links.get(i);
-                    String schema = l.schema;
-                    _postBodyBuffer.append("<a ");
-                    _postBodyBuffer.append(getClass("summDetailExternalLink")).append(" href=\"externallink.jsp?");
-                    if (l.schema != null)
-                            _postBodyBuffer.append("schema=").append(sanitizeURL(l.schema)).append('&');
-                    if (l.location != null)
-                            _postBodyBuffer.append("location=").append(sanitizeURL(l.location)).append('&');
-                    _postBodyBuffer.append("\" title=\"Goto ").append(sanitizeTagParam(l.location));
-                    _postBodyBuffer.append("\">").append(sanitizeString(l.location, 30));
-                    _postBodyBuffer.append(getSpan("summDetailExternalNet")).append(" (").append(sanitizeString(l.schema)).append(")</span></a> ");
-                }
-                _postBodyBuffer.append("<br />\n");
-            }
-
-            if (_addresses.size() > 0) {
-                _postBodyBuffer.append(getSpan("summDetailAddr")).append("Addresses:</span>");
-                for (int i = 0; i < _addresses.size(); i++) {
-                    Address a = (Address)_addresses.get(i);
-                    importAddress(a);
-                    PetName pn = null;
-                    if (_user != null)
-                        pn = _user.getPetNameDB().getByLocation(a.location);
-                    if (pn != null) {
-                        _postBodyBuffer.append(' ').append(getSpan("summDetailAddrKnown"));
-                        _postBodyBuffer.append(sanitizeString(pn.getName())).append("</span>");
-                    } else {
-                        _postBodyBuffer.append(" <a ").append(getClass("summDetailAddrLink")).append(" href=\"addresses.jsp?");
-                        if (a.schema != null)
-                            _postBodyBuffer.append(AddressesServlet.PARAM_NET).append('=').append(sanitizeTagParam(a.schema)).append('&');
-                        if (a.location != null)
-                            _postBodyBuffer.append(AddressesServlet.PARAM_LOC).append('=').append(sanitizeTagParam(a.location)).append('&');
-                        if (a.name != null)
-                            _postBodyBuffer.append(AddressesServlet.PARAM_NAME).append('=').append(sanitizeTagParam(a.name)).append('&');
-                        if (a.protocol != null)
-                            _postBodyBuffer.append(AddressesServlet.PARAM_PROTO).append('=').append(sanitizeTagParam(a.protocol)).append('&');
-                        _postBodyBuffer.append("\">").append(sanitizeString(a.name)).append("</a>");
-                    }                    
-                }
-                _postBodyBuffer.append("<br />\n");
-            }
-
-            if (_archives.size() > 0) {
-                _postBodyBuffer.append(getSpan("summDetailArchive")).append("Archives:</span>");
-                for (int i = 0; i < _archives.size(); i++) {
-                    ArchiveRef a = (ArchiveRef)_archives.get(i);
-                    _postBodyBuffer.append(" <a ").append(getClass("summDetailArchiveLink")).append(" href=\"").append(getArchiveURL(null, new SafeURL(a.locationSchema + "://" + a.location)));
-                    _postBodyBuffer.append("\">").append(sanitizeString(a.name)).append("</a>");
-                    if (a.description != null)
-                        _postBodyBuffer.append(": ").append(getSpan("summDetailArchiveDesc")).append(sanitizeString(a.description)).append("</span>");
-                    if (null == _user.getPetNameDB().getByLocation(a.location)) {
-                        _postBodyBuffer.append(" <a ").append(getClass("summDetailArchiveBookmark")).append(" href=\"");
-                        _postBodyBuffer.append(getBookmarkURL(a.name, a.location, a.locationSchema, AddressesServlet.PROTO_ARCHIVE));
-                        _postBodyBuffer.append("\">bookmark it</a>");
-                    }
-                }
-                _postBodyBuffer.append("<br />\n");
-            }
-
-            if (_entry != null) {
-                List replies = _archive.getIndex().getReplies(_entry.getURI());
-                if ( (replies != null) && (replies.size() > 0) ) {
-                    _postBodyBuffer.append(getSpan("summDetailReplies")).append("Replies:</span> ");
-                    for (int i = 0; i < replies.size(); i++) { 
-                        BlogURI reply = (BlogURI)replies.get(i);
-                        _postBodyBuffer.append("<a ").append(getClass("summDetailReplyLink")).append(" href=\"");
-                        _postBodyBuffer.append(getPageURL(reply.getKeyHash(), null, reply.getEntryId(), -1, -1, true, _user.getShowImages()));
-                        _postBodyBuffer.append("\">");
-                        _postBodyBuffer.append(getSpan("summDetailReplyAuthor"));
-                        BlogInfo replyAuthor = _archive.getBlogInfo(reply);
-                        if (replyAuthor != null) {
-                            _postBodyBuffer.append(sanitizeString(replyAuthor.getProperty(BlogInfo.NAME)));
-                        } else {
-                            _postBodyBuffer.append(reply.getKeyHash().toBase64().substring(0,16));
-                        }
-                        _postBodyBuffer.append("</span> on ");
-                        _postBodyBuffer.append(getSpan("summDetailReplyDate"));
-                        _postBodyBuffer.append(getEntryDate(reply.getEntryId()));
-                        _postBodyBuffer.append("</a></span> ");
-                    }
-                    _postBodyBuffer.append("<br />");
-                }
-            }
-        
-            String inReplyTo = (String)_headers.get(HEADER_IN_REPLY_TO);
-            if ( (inReplyTo != null) && (inReplyTo.trim().length() > 0) ) {
-                _postBodyBuffer.append(" <a ").append(getClass("summDetailParent")).append(" href=\"threads.jsp?").append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(sanitizeTagParam(inReplyTo)).append("\">(view parent)</a>\n");
-            }
-                
-            _postBodyBuffer.append("</td>\n</form>\n</tr>\n");
-            _postBodyBuffer.append("<!-- end of the post summary details -->\n");
-        }
-        _postBodyBuffer.append("</table>\n");
-    }
-    
-    public void receiveHeader(String header, String value) { 
-        //System.err.println("Receive header [" + header + "] = [" + value + "]");
-        if (HEADER_PETNAME.equals(header)) {
-            StringTokenizer tok = new StringTokenizer(value, "\t\n");
-            if (tok.countTokens() != 4)
-                return;
-            String name = tok.nextToken();
-            String net = tok.nextToken();
-            String proto = tok.nextToken();
-            String loc = tok.nextToken();
-            Address a = new Address();
-            a.name = sanitizeString(name, false);
-            a.schema = sanitizeString(net, false);
-            a.protocol = sanitizeString(proto, false);
-            a.location = sanitizeString(loc, false);
-            _addresses.add(a);
-        } else {
-            _headers.put(header, value); 
-        }
-    }
-    
-    public void receiveHeaderEnd() {
-        _preBodyBuffer.append("<table ").append(getClass("overall")).append(" width=\"100%\" border=\"0\">\n");
-        renderSubjectCell();
-        renderMetaCell();
-        renderPreBodyCell();
-    }
-    
-    public static final String HEADER_SUBJECT = "Subject";
-    public static final String HEADER_BGCOLOR = "bgcolor";
-    public static final String HEADER_IN_REPLY_TO = "InReplyTo";
-    public static final String HEADER_STYLE = "Style";
-    public static final String HEADER_PETNAME = "PetName";
-    public static final String HEADER_TAGS = "Tags";
-    /** if set to true, don't display the message in the same thread, though keep a parent reference */
-    public static final String HEADER_FORCE_NEW_THREAD = "ForceNewThread";
-    /** if set to true, don't let anyone else reply in the same thread (but let the original author reply) */
-    public static final String HEADER_REFUSE_REPLIES = "RefuseReplies";
-    
-    private void renderSubjectCell() {
-        _preBodyBuffer.append("<form action=\"index.jsp\">");
-        _preBodyBuffer.append("<tr ").append(getClass("subject")).append(">");
-        _preBodyBuffer.append("<td ").append(getClass("subject")).append(" align=\"left\" valign=\"top\" width=\"400\"> ");
-        String subject = (String)_headers.get(HEADER_SUBJECT);
-        if (subject == null)
-            subject = "[no subject]";
-        _preBodyBuffer.append(getSpan("subjectText")).append(sanitizeString(subject));
-        _preBodyBuffer.append("</span></td>\n");
-    }
-    
-    private void renderPreBodyCell() {
-        _preBodyBuffer.append("</form>");
-        String bgcolor = (String)_headers.get(HEADER_BGCOLOR);
-        _preBodyBuffer.append("<tr ").append(getClass("body")).append(" >");
-        _preBodyBuffer.append("<td colspan=\"2\" align=\"left\" valign=\"top\" ").append(getClass("body"));
-        _preBodyBuffer.append((bgcolor != null ? " bgcolor=\"" + sanitizeTagParam(bgcolor) + "\"" : "") + ">");
-    }
-    
-    private void renderMetaPetname(PetName pn, BlogInfo info) {        
-        if (info != null) {
-            _preBodyBuffer.append("<a ").append(getClass("metaLink")).append(" href=\"").append(getMetadataURL()).append("\">");
-            if (pn != null) {
-                _preBodyBuffer.append(getSpan("metaKnown")).append(sanitizeString(pn.getName())).append("</span>");
-            } else {
-                String nameStr = info.getProperty("Name");
-                if (nameStr == null)
-                    _preBodyBuffer.append(getSpan("metaUnknown")).append("[no name]</span>");
-                else
-                    _preBodyBuffer.append(getSpan("metaUnknown")).append(sanitizeString(nameStr)).append("</span>");
-            }
-            _preBodyBuffer.append("</a>");
-        } else {
-            _preBodyBuffer.append(getSpan("metaUnknown")).append("[unknown blog]</span>");
-        }
-    }
-    
-    protected void renderMetaCell() {
-        String tags[] = (_entry != null ? _entry.getTags() : null);
-        _preBodyBuffer.append("<td nowrap=\"nowrap\" align=\"right\" valign=\"top\" ");
-        _preBodyBuffer.append(getClass("meta")).append(">\n");
-        
-        PetName pn = null;
-        if ( (_entry != null) && (_user != null) )
-            pn = _user.getPetNameDB().getByLocation(_entry.getURI().getKeyHash().toBase64());
-        //if (knownName != null)
-        //    _preBodyBuffer.append("Pet name: ").append(sanitizeString(knownName)).append(" ");
-
-        BlogInfo info = null;
-        if (_entry != null) 
-            info = _archive.getBlogInfo(_entry.getURI());
-        renderMetaPetname(pn, info);
-        
-        if ( (_user != null) && (_user.getAuthenticated()) && (_entry != null) ) {
-            if ( (pn == null) || (!pn.isMember("Favorites")) )
-                _preBodyBuffer.append(" <input ").append(getClass("bookmark")).append(" type=\"submit\" name=\"action\" value=\"Bookmark blog\" />");
-            if ( (pn == null) || (!pn.isMember("Ignore")) )
-                _preBodyBuffer.append(" <input ").append(getClass("ignore")).append(" type=\"submit\" name=\"action\" value=\"Ignore blog\" />");
-            else
-                _preBodyBuffer.append(" <input ").append(getClass("unignore")).append(" type=\"submit\" name=\"action\" value=\"Unignore blog\" />");
-            _preBodyBuffer.append(" <input type=\"hidden\" name=\"blog\" value=\"").append(_entry.getURI().getKeyHash().toBase64()).append("\" />");
-            if (info != null)
-                _preBodyBuffer.append(" <input type=\"hidden\" name=\"name\" value=\"").append(sanitizeTagParam(info.getProperty("Name"))).append("\" />");
-        }
-
-        
-        if ( (tags != null) && (tags.length > 0) ) {
-            _preBodyBuffer.append(getSpan("metaTags")).append(" Tags: ");
-            _preBodyBuffer.append("<select ").append(getClass("metaTagList")).append(" name=\"selector\">");
-            for (int i = 0; tags != null && i < tags.length; i++) {
-                _preBodyBuffer.append("<option value=\"blogtag://");
-                _preBodyBuffer.append(_entry.getURI().getKeyHash().toBase64());
-                _preBodyBuffer.append('/').append(Base64.encode(DataHelper.getUTF8(tags[i]))).append("\">");
-                _preBodyBuffer.append(sanitizeString(tags[i]));
-                _preBodyBuffer.append("</option>\n");
-                /*
-                _preBodyBuffer.append("<a href=\"");
-                _preBodyBuffer.append(getPageURL(_entry.getURI().getKeyHash(), tags[i], -1, -1, -1, (_user != null ? _user.getShowExpanded() : false), (_user != null ? _user.getShowImages() : false)));
-                _preBodyBuffer.append("\">");
-                _preBodyBuffer.append(sanitizeString(tags[i]));
-                _preBodyBuffer.append("</a>");
-                if (i + 1 < tags.length)
-                    _preBodyBuffer.append(", ");
-                 */
-            }
-            _preBodyBuffer.append("</select>");
-            _preBodyBuffer.append("<input ").append(getClass("metaTagView")).append(" type=\"submit\" value=\"View\" /></span>\n");
-            //_preBodyBuffer.append("</i>");
-        }
-        _preBodyBuffer.append(" ");
-        /*
-        String inReplyTo = (String)_headers.get(HEADER_IN_REPLY_TO);
-        if ( (inReplyTo != null) && (inReplyTo.trim().length() > 0) )
-            _preBodyBuffer.append(" <a href=\"").append(getPageURL(sanitizeTagParam(inReplyTo))).append("\">In reply to</a>\n");
-         */
-        
-        _preBodyBuffer.append(getSpan("metaDate"));
-        if (_entry != null)
-            _preBodyBuffer.append(getEntryDate(_entry.getURI().getEntryId()));
-        else
-            _preBodyBuffer.append(getEntryDate(new Date().getTime()));
-        _preBodyBuffer.append("</span>");
-        
-        if ( (_user != null) && (_user.getAuthenticated()) ) {
-            _preBodyBuffer.append(" <a ").append(getClass("replyLink"));
-            String subject = (String)_headers.get(HEADER_SUBJECT);
-            if (subject != null) {
-                if (!subject.startsWith("re:"))
-                    subject = "re: " + subject;
-            } else {
-                subject = "re: ";
-            }
-            StringBuffer tagStr = new StringBuffer(32);
-            if ( (tags != null) && (tags.length > 0) )
-                for (int i = 0; i < tags.length; i++)
-                    tagStr.append(tags[i]).append('\t');
-            String replyURL = getPostURL(_user.getBlog(), true, subject, tagStr.toString());
-            _preBodyBuffer.append(" href=\"").append(replyURL).append("\">Reply</a>\n");
-        }
-        _preBodyBuffer.append("\n</td>");
-        _preBodyBuffer.append("</tr>\n");
-    }
-    
-    private final SimpleDateFormat _dateFormat = new SimpleDateFormat("yyyy/MM/dd", Locale.UK);
-    public final String getEntryDate(long when) {
-        synchronized (_dateFormat) {
-            try {
-                String str = _dateFormat.format(new Date(when));
-                long dayBegin = _dateFormat.parse(str).getTime();
-                return str + " [" + (when - dayBegin) + "]";
-            } catch (ParseException pe) {
-                if (_log.shouldLog(Log.WARN))
-                    _log.warn("Error formatting", pe);
-                // wtf
-                return "unknown";
-            }
-        }
-    }
-    
-    public static final String sanitizeString(String str) { return sanitizeString(str, true); }
-    public static final String sanitizeString(String str, int maxLen) { return sanitizeString(str, true, maxLen); }
-    public static final String sanitizeString(String str, boolean allowNL) { return sanitizeString(str, allowNL, -1); }
-    public static final String sanitizeString(String str, boolean allowNL, int maxLen) {
-        if (str == null) return null;
-        boolean unsafe = false;
-        unsafe = unsafe || str.indexOf('<') >= 0;
-        unsafe = unsafe || str.indexOf('>') >= 0;
-        if (!allowNL) {
-            unsafe = unsafe || str.indexOf('\n') >= 0;
-            unsafe = unsafe || str.indexOf('\r') >= 0;
-            unsafe = unsafe || str.indexOf('\f') >= 0;
-        }
-        if (unsafe) {
-            //str = str.replace('<', '_'); // this should be &lt;
-            //str = str.replace('>', '-'); // this should be &gt;
-            str = str.replaceAll("<", "&lt;");
-            str = str.replaceAll(">", "&gt;");
-            if (!allowNL) {
-                //str = str.replace('\n', ' ');
-                //str = str.replace('\r', ' ');
-                //str = str.replace('\f', ' ');
-                str = str.replaceAll("\n", "<br />"); // no class
-                str = str.replaceAll("\r", "<br />"); // no class
-                str = str.replaceAll("\f", "<br />"); // no class
-            }
-        }
-        if ( (maxLen > 0) && (str.length() > maxLen) )
-            return str.substring(0, maxLen) + "...";
-        else
-            return str;
-    }
-
-    public static final String sanitizeURL(String str) { 
-        if (str == null) return "";
-        return Base64.encode(DataHelper.getUTF8(str)); 
-    }
-    public static final String sanitizeTagParam(String str) {
-        if (str == null) return "";
-        //str = str.replace('&', '_'); // this should be &amp;
-        str = str.replaceAll("&", "&amp;");
-        
-        if (str.indexOf("\"") < 0 && str.indexOf("'") < 0)
-            return sanitizeString(str);
-        
-        str = str.replaceAll("\"", "&quot;");
-        str = str.replaceAll("'", "&#39;"); // as &apos;, but supported by IE
-        
-        return sanitizeString(str);
-    }
-    
-    public static final String sanitizeXML(String orig) {
-        if (orig == null) return "";
-        if (orig.indexOf('&') < 0) return orig;
-        if (true) return orig.replaceAll("&", "&amp;");
-        StringBuffer rv = new StringBuffer(orig.length()+32);
-        for (int i = 0; i < orig.length(); i++) {
-            if (orig.charAt(i) == '&')
-                rv.append("&amp;");
-            else
-                rv.append(orig.charAt(i));
-        }
-        return rv.toString();
-    }
-    public static final String sanitizeXML(StringBuffer orig) {
-        if (orig == null) return "";
-        if (orig.indexOf("&") >= 0) 
-            return orig.toString().replaceAll("&", "&amp;");
-        else
-            return orig.toString();
-    }
-    public static final String sanitizeStrippedXML(String orig) {
-        if (orig == null) return "";
-        orig = orig.replaceAll("&", "&amp;");
-        orig = orig.replaceAll("<", "&lt;");
-        orig = orig.replaceAll(">", "&gt;");
-        return orig;
-    }
-
-    private static final String STYLE_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
-    public static String sanitizeStyle(String style) {
-        if ( (style == null) || (style.trim().length() <= 0) ) return null;
-        char c[] = style.toCharArray();
-        for (int i = 0; i < c.length; i++)
-            if (STYLE_CHARS.indexOf(c[i]) < 0)
-                c[i] = '_';
-        return new String(c);
-    }
-        
-    protected String getEntryURL() { return getEntryURL(_user != null ? _user.getShowImages() : false); }
-    protected String getEntryURL(boolean showImages) {
-        if (_entry == null) return "unknown";
-        return "threads.jsp?" + ThreadedHTMLRenderer.PARAM_AUTHOR + '=' +
-               Base64.encode(_entry.getURI().getKeyHash().getData()) + '&' +
-               ThreadedHTMLRenderer.PARAM_VIEW_POST + '=' + _entry.getURI().getKeyHash().toBase64() + '/' + 
-               _entry.getURI().getEntryId();
-    }
-
-    protected String getAttachmentURLBase() { return "viewattachment.jsp?"; }
-    protected String getAttachmentURL(int id) {
-        if (_entry == null) return "unknown";
-        return getAttachmentURLBase() +
-               ArchiveViewerBean.PARAM_BLOG + "=" +
-               Base64.encode(_entry.getURI().getKeyHash().getData()) +
-               "&" + ArchiveViewerBean.PARAM_ENTRY + "=" + _entry.getURI().getEntryId() +
-               "&" + ArchiveViewerBean.PARAM_ATTACHMENT + "=" + id;
-    }
-    
-    public String getMetadataURL() { 
-        if (_entry == null) return "unknown";
-        return getMetadataURL(_entry.getURI().getKeyHash()); 
-    }
-    public String getMetadataURL(Hash blog) {
-        return "viewmetadata.jsp?" + ArchiveViewerBean.PARAM_BLOG + "=" +
-               Base64.encode(blog.getData());
-    }
-
-    public String getPostURL(Hash blog) {
-        return "post.jsp?" + ArchiveViewerBean.PARAM_BLOG + "=" + Base64.encode(blog.getData());
-    }
-    public String getPostURL(Hash blog, boolean asReply, String subject, String tags) { 
-        if (asReply && _entry != null) {
-            StringBuffer rv = new StringBuffer(128);
-            rv.append("post.jsp?").append(ArchiveViewerBean.PARAM_BLOG).append("=").append(Base64.encode(blog.getData()));
-            rv.append('&').append(PostServlet.PARAM_PARENT).append('=');
-            rv.append(Base64.encode("entry://" + _entry.getURI().getKeyHash().toBase64() + "/" + _entry.getURI().getEntryId()));
-            if (subject != null)
-                rv.append('&').append(ArchiveViewerBean.PARAM_SUBJECT).append('=').append(Base64.encode(subject));
-            if (tags != null)
-                rv.append('&').append(ArchiveViewerBean.PARAM_TAGS).append('=').append(Base64.encode(tags));
-            rv.append('&').append(ArchiveViewerBean.PARAM_PARENT).append('=').append(Base64.encode(_entry.getURI().toString()));
-            return rv.toString();
-        } else {
-            return getPostURL(blog);
-        }
-    }
-    
-    /**
-     * entry may take the form of "base64/messageId", "entry://base64/messageId", or "blog://base64/messageId"
-     *
-     */
-    public String getPageURL(String entry) {
-        StringBuffer buf = new StringBuffer(128);
-        buf.append("threads.jsp?");
-        if (entry != null) {
-            if (entry.startsWith("entry://"))
-                entry = entry.substring("entry://".length());
-            else if (entry.startsWith("blog://"))
-                entry = entry.substring("blog://".length());
-            if (entry.length() > 0) {
-                buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(entry).append('&');
-                buf.append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=').append(entry).append('&');
-            }
-        }
-        return buf.toString();
-    }
-
-    public String getPageURL(Hash blog, String tag, long entryId, int numPerPage, int pageNum, boolean expandEntries, boolean showImages) {
-        return getPageURL(blog, tag, entryId, null, numPerPage, pageNum, expandEntries, showImages);
-    }
-    public String getPageURL(Hash blog, String tag, long entryId, String group, int numPerPage, int pageNum, boolean expandEntries, boolean showImages) {
-        StringBuffer buf = new StringBuffer(128);
-        buf.append("threads.jsp?");
-        if (blog != null)
-            buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(blog.toBase64()).append('&');
-        if (tag != null)
-            buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(sanitizeTagParam(tag)).append('&');
-        String entry = null;
-        if (entryId >= 0) {
-            entry = blog.toBase64() + '/' + entryId;
-            buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(entry).append('&');
-            buf.append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=').append(entry).append('&');
-        }   
-        if ( (pageNum >= 0) && (numPerPage > 0) )
-            buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(pageNum*numPerPage).append('&');
-        return buf.toString();
-    }
-    public static String getArchiveURL(Hash blog, SafeURL archiveLocation) {
-        return "syndicate.jsp?" 
-               //+ "action=Continue..." // should this be the case?
-               + "&" + SyndicateServlet.PARAM_SCHEMA + "=" + sanitizeTagParam(archiveLocation.getSchema()) 
-               + "&" + SyndicateServlet.PARAM_LOCATION + "=" + sanitizeTagParam(archiveLocation.getLocation());
-    }
-    public static String getBookmarkURL(String name, String location, String schema, String protocol) {
-        return "addresses.jsp?" + AddressesServlet.PARAM_NAME + '=' + sanitizeTagParam(name)
-               + "&" + AddressesServlet.PARAM_NET + '=' + sanitizeTagParam(schema)
-               + "&" + AddressesServlet.PARAM_PROTO + '=' + sanitizeTagParam(protocol)
-               + "&" + AddressesServlet.PARAM_LOC + '=' + sanitizeTagParam(location);
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/Link.java b/apps/syndie/java/src/net/i2p/syndie/sml/Link.java
deleted file mode 100644
index c343216f74ed3a5c05b18372cd3878951043c856..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/sml/Link.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package net.i2p.syndie.sml;
-
-import net.i2p.data.DataHelper;
-
-/** contains intermediary rendering state */
-class Link {
-    public String schema;
-    public String location;
-    public int hashCode() { return -1; }
-    public boolean equals(Object o) {
-        Link l = (Link)o;
-        return DataHelper.eq(schema, l.schema) && DataHelper.eq(location, l.location);
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/RSSRenderer.java b/apps/syndie/java/src/net/i2p/syndie/sml/RSSRenderer.java
deleted file mode 100644
index 8e99a50bf37c83b2a49198d2bc5da22fffd5f1aa..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/sml/RSSRenderer.java
+++ /dev/null
@@ -1,341 +0,0 @@
-package net.i2p.syndie.sml;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.List;
-
-import net.i2p.I2PAppContext;
-import net.i2p.client.naming.PetName;
-import net.i2p.data.Base64;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.Attachment;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.EntryContainer;
-
-/**
- *
- */
-public class RSSRenderer extends HTMLRenderer {
-    
-    public RSSRenderer(I2PAppContext ctx) {
-        super(ctx);
-    }
-    
-    private static final boolean RSS_EXCERPT_ONLY = false;
-    
-    public void render(User user, Archive archive, EntryContainer entry, String urlPrefix, Writer out) throws IOException {
-        if (entry == null) return;
-        prepare(user, archive, entry, entry.getEntry().getText(), out, RSS_EXCERPT_ONLY, false);
-        BlogInfo info = archive.getBlogInfo(entry.getURI());
-        
-        out.write("   <item>\n");
-        String subject = sanitizeXML(sanitizeString((String)_headers.get(HEADER_SUBJECT)));
-        if ( (subject == null) || (subject.length() <= 0) )
-            subject = "not specified";
-        out.write("    <title>" + subject + "</title>\n");
-        out.write("    <link>" + urlPrefix + BlogRenderer.getEntryURL(entry, info, true) + "</link>\n");
-        out.write("    <guid isPermalink=\"false\">syndie://" + entry.getURI().toString() + "</guid>\n");
-        out.write("    <pubDate>" + getRFC822Date(entry.getURI().getEntryId()) + "</pubDate>\n");
-        PetName pn = user.getPetNameDB().getByLocation(entry.getURI().getKeyHash().toBase64());
-        String author = null;
-        if (pn != null)
-            author = pn.getName();
-        if (author == null) {
-            if (info != null)
-                author = info.getProperty(BlogInfo.NAME);
-        }
-        if (author == null)
-            author = entry.getURI().getKeyHash().toBase64();
-        out.write("    <author>" + sanitizeXML(sanitizeString(author)) + "@syndie.invalid</author>\n");
-        String tags[] = entry.getTags();
-        if (tags != null) 
-            for (int i = 0; i < tags.length; i++) 
-                out.write("    <category>" + sanitizeXML(sanitizeString(tags[i])) + "</category>\n");
-        
-        out.write("    <description>" + sanitizeXML(_bodyBuffer.toString()) + "</description>\n");
-
-        renderEnclosures(user, entry, urlPrefix, out);
-        
-        out.write("   </item>\n");
-    }
-    
-    
-    public void receiveBold(String text) { 
-        if (!continueBody()) { return; }
-        _bodyBuffer.append(sanitizeString(text));
-    }
-    public void receiveItalic(String text) { 
-        if (!continueBody()) { return; }
-        _bodyBuffer.append(sanitizeString(text));
-    }
-    public void receiveUnderline(String text) { 
-        if (!continueBody()) { return; }
-        _bodyBuffer.append(sanitizeString(text));
-    }
-    public void receiveHR() {
-        if (!continueBody()) { return; }
-    }
-    public void receiveH1(String body) {
-        if (!continueBody()) { return; }
-        _bodyBuffer.append(sanitizeString(body));
-    }
-    public void receiveH2(String body) {
-        if (!continueBody()) { return; }
-        _bodyBuffer.append(sanitizeString(body));
-    }
-    public void receiveH3(String body) {
-        if (!continueBody()) { return; }
-        _bodyBuffer.append(sanitizeString(body));
-    }
-    public void receiveH4(String body) {
-        if (!continueBody()) { return; }
-        _bodyBuffer.append(sanitizeString(body));
-    }
-    public void receiveH5(String body) {
-        if (!continueBody()) { return; }
-        _bodyBuffer.append(sanitizeString(body));
-    }
-    public void receivePre(String body) {
-        if (!continueBody()) { return; }
-        _bodyBuffer.append(sanitizeString(body));
-    }
-    
-    public void receiveQuote(String text, String whoQuoted, String quoteLocationSchema, String quoteLocation) {
-        if (!continueBody()) { return; }
-        _bodyBuffer.append(sanitizeString(text));
-    }
-    public void receiveCode(String text, String codeLocationSchema, String codeLocation) { 
-        if (!continueBody()) { return; }
-           _bodyBuffer.append(sanitizeString(text));
-    }
-    public void receiveImage(String alternateText, int attachmentId) {
-        if (!continueBody()) { return; }
-        _bodyBuffer.append(sanitizeString(alternateText));
-    }
-    public void receiveCut(String summaryText) { 
-        if (!continueBody()) { return; }
-        _cutReached = true;
-        if (_cutBody) {
-            if ( (summaryText != null) && (summaryText.length() > 0) )
-                _bodyBuffer.append(sanitizeString(summaryText));
-            else
-                _bodyBuffer.append("more inside...");
-        } else {
-            if (summaryText != null)
-                _bodyBuffer.append(sanitizeString(summaryText));
-        }
-    }
-    /** are we either before the cut or rendering without cutting? */
-    protected boolean continueBody() {
-        boolean rv = ( (!_cutReached) && (_bodyBuffer.length() <= _cutSize) ) || (!_cutBody);
-        //if (!rv) 
-        //    System.out.println("rv: " + rv + " Cut reached: " + _cutReached + " bodyBufferSize: " + _bodyBuffer.length() + " cutBody? " + _cutBody);
-        if (!rv && !_cutReached) {
-            // exceeded the allowed size
-            _bodyBuffer.append("more inside...");
-            _cutReached = true;
-        }
-        return rv;
-    }
-    public void receiveNewline() { 
-        if (!continueBody()) { return; }
-        if (true || (_lastNewlineAt >= _bodyBuffer.length()))
-            _bodyBuffer.append("\n");
-        else
-            _lastNewlineAt = _bodyBuffer.length();
-    }
-    public void receiveBlog(String name, String hash, String tag, long entryId, List locations, String description) {
-        byte blogData[] = Base64.decode(hash);
-        if ( (blogData == null) || (blogData.length != Hash.HASH_LENGTH) )
-            return;
-    
-        Blog b = new Blog();
-        b.name = name;
-        b.hash = hash;
-        b.tag = tag;
-        b.entryId = entryId;
-        b.locations = locations;
-        if (!_blogs.contains(b))
-            _blogs.add(b);
-    
-        if (!continueBody()) { return; }
-        if (hash == null) return;
-        
-        Hash blog = new Hash(blogData);
-        if ( (description != null) && (description.trim().length() > 0) ) {
-            _bodyBuffer.append(sanitizeString(description));
-        } else if ( (name != null) && (name.trim().length() > 0) ) {
-            _bodyBuffer.append(sanitizeTagParam(name));
-        } else {
-            _bodyBuffer.append("[view entry]");
-        }
-    }
-    public void receiveArchive(String name, String description, String locationSchema, String location, 
-                               String postingKey, String anchorText) {        
-        ArchiveRef a = new ArchiveRef();
-        a.name = name;
-        a.description = description;
-        a.locationSchema = locationSchema;
-        a.location = location;
-        if (!_archives.contains(a))
-            _archives.add(a);
-    
-        if (!continueBody()) { return; }
-        
-        _bodyBuffer.append(sanitizeString(anchorText));
-    }
-    public void receiveLink(String schema, String location, String text) {
-        Link l = new Link();
-        l.schema = schema;
-        l.location = location;
-        if (!_links.contains(l))
-            _links.add(l);
-        if (!continueBody()) { return; }
-        if ( (schema == null) || (location == null) ) return;
-        _bodyBuffer.append(sanitizeString(text));
-    }
-    public void receiveAddress(String name, String schema, String protocol, String location, String anchorText) {
-        Address a = new Address();
-        a.name = name;
-        a.schema = schema;
-        a.location = location;
-        a.protocol = protocol;
-        if (!_addresses.contains(a))
-            _addresses.add(a);
-        if (!continueBody()) { return; }
-        if ( (schema == null) || (location == null) ) return;
-        PetName pn = null;
-        if (_user != null)
-            pn = _user.getPetNameDB().getByLocation(location);
-        if (pn != null) {
-            _bodyBuffer.append(sanitizeString(anchorText));
-        } else {
-            _bodyBuffer.append(sanitizeString(anchorText));
-        }
-    }
-    public void receiveAttachment(int id, int thumb, String anchorText) {
-        if (!continueBody()) { return; }
-        _bodyBuffer.append(sanitizeString(anchorText));
-    }
-    
-    // Mon, 03 Jun 2005 13:04:11 +0000
-    private static final SimpleDateFormat _rfc822Date = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
-    private static final String getRFC822Date(long when) {
-        synchronized (_rfc822Date) {
-            return _rfc822Date.format(new Date(when));
-        }
-    }
-    
-    private void renderEnclosures(User user, EntryContainer entry, String urlPrefix, Writer out) throws IOException {
-        int included = 0;
-        if (entry.getAttachments() != null) {
-            for (int i = 0; i < _entry.getAttachments().length; i++) {
-                Attachment a = _entry.getAttachments()[i];
-                String url = urlPrefix + sanitizeXML(getAttachmentURL(i)) 
-                             + "#" + sanitizeTagParam(a.getName()); // tacked on for readability
-                out.write("    <media:content url=\"" + url
-                               + "\" fileSize=\"" + a.getDataLength()
-                               + "\" type=\"" + sanitizeTagParam(a.getMimeType()) 
-                               + "\">");
-                // we can do neat stuff with Media RSS (http://search.yahoo.com/mrss) here, such as
-                // include descriptions, titles, keywords, thumbnails, etc
-                out.write("    </media:content>\n");
-
-                if (included == 0) // plain RSS enclosures can only have one enclosure per entry, unlike Media RSS
-                    out.write("    <enclosure url=\"" + url
-                                   + "\" length=\"" + a.getDataLength() 
-                                   + "\" type=\"" + sanitizeTagParam(a.getMimeType()) + "\" syndietype=\"attachment\" />\n");
-                included++;
-            }
-        }
-
-        /*
-        if (_blogs.size() > 0) {
-            for (int i = 0; i < _blogs.size(); i++) {
-                Blog b = (Blog)_blogs.get(i);
-                out.write("    <enclosure url=\"" + urlPrefix + 
-                               sanitizeXML(getPageURL(new Hash(Base64.decode(b.hash)), b.tag, b.entryId, 
-                                          -1, -1, (_user != null ? _user.getShowExpanded() : false), 
-                                          (_user != null ? _user.getShowImages() : false)))
-                               + "\" length=\"1\" type=\"text/html\" syndietype=\"blog\" />\n");
-            }
-        }
-
-        if (_links.size() > 0) {
-            for (int i = 0; i < _links.size(); i++) {
-                Link l = (Link)_links.get(i);
-                StringBuffer url = new StringBuffer(128);
-                url.append("externallink.jsp?schema=");
-                url.append(sanitizeURL(l.schema)).append("&location=");
-                url.append(sanitizeURL(l.location));
-                out.write("    <enclosure url=\"" + urlPrefix + sanitizeXML(url) + "\" length=\"1\" type=\"text/html\" syndietype=\"link\" />\n");
-            }
-        }
-
-        if (_addresses.size() > 0) {
-            for (int i = 0; i < _addresses.size(); i++) {
-                Address a = (Address)_addresses.get(i);
-
-                PetName pn = null;
-                if (_user != null)
-                    pn = _user.getPetNameDB().getByLocation(a.location);
-                if (pn == null) {
-                    StringBuffer url = new StringBuffer(128);
-                    url.append("addresses.jsp?").append(AddressesServlet.PARAM_NAME).append('=');
-                    url.append(sanitizeTagParam(a.schema)).append("&").append(AddressesServlet.PARAM_LOC).append("=");
-                    url.append(sanitizeTagParam(a.location)).append("&").append(AddressesServlet.PARAM_NAME).append("=");
-                    url.append(sanitizeTagParam(a.name)).append("&").append(AddressesServlet.PARAM_PROTO).append("=");
-                    url.append(sanitizeTagParam(a.protocol));
-                    out.write("    <enclosure url=\"" + urlPrefix + sanitizeXML(url) + "\" length=\"1\" type=\"text/html\" syndietype=\"address\" />\n");
-                }
-            }
-        }
-
-        if (_archives.size() > 0) {
-            for (int i = 0; i < _archives.size(); i++) {
-                ArchiveRef a = (ArchiveRef)_archives.get(i);
-                String url = getArchiveURL(null, new SafeURL(a.locationSchema + "://" + a.location));
-                out.write("    <enclosure url=\"" + urlPrefix + sanitizeXML(url) + "\" length=\"1\" type=\"text/html\" syndietype=\"archive\" />\n");
-            }
-        }
-
-        if (_entry != null) {
-            List replies = _archive.getIndex().getReplies(_entry.getURI());
-            if ( (replies != null) && (replies.size() > 0) ) {
-                for (int i = 0; i < replies.size(); i++) { 
-                    BlogURI reply = (BlogURI)replies.get(i);
-                    String url = getPageURL(reply.getKeyHash(), null, reply.getEntryId(), -1, -1, true, _user.getShowImages());
-                    out.write("    <enclosure url=\"" + urlPrefix + sanitizeXML(url) + "\" length=\"1\" type=\"text/html\" syndietype=\"reply\" />\n");
-                }
-            }
-        }
-        
-        String inReplyTo = (String)_headers.get(HEADER_IN_REPLY_TO);
-        if ( (inReplyTo != null) && (inReplyTo.trim().length() > 0) ) {
-            String url = getPageURL(sanitizeTagParam(inReplyTo));
-            out.write("    <enclosure url=\"" + urlPrefix + sanitizeXML(url) + "\" length=\"1\" type=\"text/html\" syndietype=\"parent\" />\n");
-        } 
-         */  
-    }
-    
-    public void receiveHeaderEnd() {}
-    public void receiveEnd() {}
-    
-    public static void main(String args[]) {
-        test("");
-        test("&");
-        test("a&");
-        test("&a");
-        test("a&a");
-        test("aa&aa");
-    }
-    private static final void test(String str) {
-        StringBuffer t = new StringBuffer(str);
-        String sanitized = sanitizeXML(t);
-        System.out.println("[" + str + "] --> [" + sanitized + "]");
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/SMLParser.java b/apps/syndie/java/src/net/i2p/syndie/sml/SMLParser.java
deleted file mode 100644
index fd9d6438723b1168c2f989f939102787376de354..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/sml/SMLParser.java
+++ /dev/null
@@ -1,472 +0,0 @@
-package net.i2p.syndie.sml;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import net.i2p.I2PAppContext;
-import net.i2p.syndie.data.SafeURL;
-import net.i2p.util.Log;
-
-/**
- * Parse out the SML from the text, firing off info to the receiver whenever certain 
- * elements are available.  This is a very simple parser, with no support for nested
- * tags.  A simple stack would be good to add, but DTSTTCPW.
- * 
- * 
- */
-public class SMLParser {
-    private Log _log;
-    private static final char TAG_BEGIN = '[';
-    private static final char TAG_END = ']';
-    private static final char LT = '<';
-    private static final char GT = '>';
-    private static final char EQ = '=';
-    private static final char DQUOTE = '"';
-    private static final char QUOTE = '\'';
-    private static final String WHITESPACE = " \t\n\r";
-    private static final char NL = '\n';
-    private static final char CR = '\n';
-    private static final char LF = '\f';
-    
-    public SMLParser(I2PAppContext ctx) {
-        _log = ctx.logManager().getLog(SMLParser.class);
-    }
-
-    public void parse(String rawSML, EventReceiver receiver) {
-        receiver.receiveBegin();
-        int off = 0;
-        off = parseHeaders(rawSML, off, receiver);
-        receiver.receiveHeaderEnd();
-        parseBody(rawSML, off, receiver);
-        receiver.receiveEnd();
-    }
-    
-    private int parseHeaders(String rawSML, int off, EventReceiver receiver) {
-        if (rawSML == null) return off;
-        int len = rawSML.length();
-        if (len == off) return off;
-        int keyBegin = off;
-        int valBegin = -1;
-        while (off < len) { 
-            char c = rawSML.charAt(off);
-            if ( (c == ':') && (valBegin < 0) ) {
-                // moving on to the value
-                valBegin = off + 1;
-            } else if (c == '\n') {
-                if (valBegin < 0) {
-                    // end of the headers
-                    off++;
-                    break;
-                } else {
-                    String key = rawSML.substring(keyBegin, valBegin-1);
-                    String val = rawSML.substring(valBegin, off);
-                    receiver.receiveHeader(key.trim(), val.trim());
-                    valBegin = -1;
-                    keyBegin = off + 1;
-                }
-            }
-            off++;
-        }
-        if ( (off >= len) && (valBegin > 0) ) {
-            String key = rawSML.substring(keyBegin, valBegin-1);
-            String val = rawSML.substring(valBegin, len);
-            receiver.receiveHeader(key.trim(), val.trim());
-        }
-        return off;
-    }
-    
-    private void parseBody(String rawSMLBody, int off, EventReceiver receiver) {
-        if (rawSMLBody == null) return;
-        int begin = off;
-        int len = rawSMLBody.length();
-        if (len <= off) return;
-        int openTagBegin = -1;
-        int openTagEnd = -1;
-        int closeTagBegin = -1;
-        int closeTagEnd = -1;
-        while (off < len) {
-            char c = rawSMLBody.charAt(off);
-            if ( (c == NL) || (c == CR) || (c == LF) ) {
-                // we only deal with newlines outside of a tag, since this is a ghetto parser
-                // without a stack, and the tag event is fired only when the tag is completed.
-                if (openTagBegin < 0) {
-                    if (begin < off)
-                        receiver.receivePlain(rawSMLBody.substring(begin, off));
-                    receiver.receiveNewline();
-                    off++;
-                    begin = off;
-                    continue;
-                }
-            } else if (c == TAG_BEGIN) {
-                if ( (off + 1 < len) && (TAG_BEGIN == rawSMLBody.charAt(off+1))) {
-                    if (begin < off)
-                        receiver.receivePlain(rawSMLBody.substring(begin, off));
-                    receiver.receiveLeftBracket();
-                    off += 2;
-                    begin = off;
-                    continue;
-                } else if (openTagBegin < 0) {
-                    // push everything seen and not accounted for into a plain area
-                    if (closeTagEnd < 0) {
-                        if (begin < off)
-                            receiver.receivePlain(rawSMLBody.substring(begin, off));
-                    } else {
-                        if (closeTagEnd + 1 < off)
-                            receiver.receivePlain(rawSMLBody.substring(closeTagEnd+1, off));
-                    }
-                    openTagBegin = off;
-                    closeTagBegin = -1;
-                    begin = off + 1;
-                } else {
-                    // ok, we are at the end of the tag, process it
-                    closeTagBegin = off;
-                    while ( (c != TAG_END) && (off < len) ) {
-                        off++;
-                        c = rawSMLBody.charAt(off);
-                    }
-                    parseTag(rawSMLBody, openTagBegin, openTagEnd, closeTagBegin, off, receiver);
-                    begin = off + 1;
-                    openTagBegin = -1;
-                    openTagEnd = -1;
-                    closeTagBegin = -1;
-                    closeTagEnd = -1;
-                }
-            } else if (c == TAG_END) {
-                if ( (openTagBegin > 0) && (closeTagBegin < 0) ) {
-                    openTagEnd = off;
-                } else if ( (off + 1 < len) && (TAG_END == rawSMLBody.charAt(off+1))) {
-                    if (begin < off)
-                        receiver.receivePlain(rawSMLBody.substring(begin, off));
-                    receiver.receiveRightBracket();
-                    off += 2;
-                    begin = off;
-                    continue;
-                }
-            } else if (c == LT) {
-                // see above re: newlines inside tags for why we check openTagBegin<0
-                if (openTagBegin < 0) {
-                    if (begin < off)
-                        receiver.receivePlain(rawSMLBody.substring(begin, off));
-                    receiver.receiveLT();
-                    off++;
-                    begin = off;
-                    continue;
-                }
-            } else if (c == GT) {
-                // see above re: newlines inside tags for why we check openTagBegin<0
-                if (openTagBegin < 0) {
-                    if (begin < off)
-                        receiver.receivePlain(rawSMLBody.substring(begin, off));
-                    receiver.receiveGT();
-                    off++;
-                    begin = off;
-                    continue;
-                }
-            }
-            
-            off++;
-        }
-        if ( (off >= len) && (openTagBegin < 0) ) {
-            if (closeTagEnd < 0) {
-                if (begin < off)
-                    receiver.receivePlain(rawSMLBody.substring(begin, off));
-            } else {
-                if (closeTagEnd + 1 < off)
-                    receiver.receivePlain(rawSMLBody.substring(closeTagEnd+1, off));
-            }
-        }
-    }
-    
-    private void parseTag(String source, int openTagBegin, int openTagEnd, int closeTagBegin, int closeTagEnd, EventReceiver receiver) {
-        String tagName = getTagName(source, openTagBegin+1);
-        Map attributes = getAttributes(source, openTagBegin+1+tagName.length(), openTagEnd);
-        String body = null;
-        if (openTagEnd + 1 >= closeTagBegin)
-            body = "";
-        else
-            body = source.substring(openTagEnd+1, closeTagBegin);
-        
-        //System.out.println("Receiving tag [" + tagName + "] w/ open [" + source.substring(openTagBegin+1, openTagEnd) 
-        //                   + "], close [" + source.substring(closeTagBegin+1, closeTagEnd) + "] body [" 
-        //                   + body + "] attributes: " + attributes);
-        parseTag(tagName, attributes, body, receiver);
-    }
-    
-    private static final String T_BOLD = "b";
-    private static final String T_ITALIC = "i";
-    private static final String T_UNDERLINE = "u";
-    private static final String T_CUT = "cut";
-    private static final String T_IMAGE = "img";
-    private static final String T_QUOTE = "quote";
-    private static final String T_CODE = "code";
-    private static final String T_BLOG = "blog";
-    private static final String T_LINK = "link";
-    private static final String T_ADDRESS = "address";
-    private static final String T_H1 = "h1";
-    private static final String T_H2 = "h2";
-    private static final String T_H3 = "h3";
-    private static final String T_H4 = "h4";
-    private static final String T_H5 = "h5";
-    private static final String T_HR = "hr";
-    private static final String T_PRE = "pre";
-    private static final String T_ATTACHMENT = "attachment";
-    private static final String T_ARCHIVE = "archive";
-    
-    private static final String P_THUMBNAIL = "thumbnail";
-    private static final String P_ATTACHMENT = "attachment";
-    private static final String P_WHO_QUOTED = "author";
-    private static final String P_QUOTE_LOCATION = "location";
-    private static final String P_CODE_LOCATION = "location";
-    private static final String P_BLOG_NAME = "name";
-    private static final String P_BLOG_HASH = "bloghash";
-    private static final String P_BLOG_TAG = "blogtag";
-    private static final String P_BLOG_ENTRY = "blogentry";
-    private static final String P_LINK_LOCATION = "location";
-    private static final String P_LINK_SCHEMA = "schema";
-    private static final String P_ADDRESS_NAME = "name";
-    private static final String P_ADDRESS_LOCATION = "location";
-    private static final String P_ADDRESS_SCHEMA = "schema";
-    private static final String P_ADDRESS_PROTOCOL = "proto";
-    private static final String P_ATTACHMENT_ID = "id";
-    private static final String P_ARCHIVE_NAME = "name";
-    private static final String P_ARCHIVE_DESCRIPTION = "description";
-    private static final String P_ARCHIVE_LOCATION_SCHEMA = "schema";
-    private static final String P_ARCHIVE_LOCATION = "location";
-    private static final String P_ARCHIVE_POSTING_KEY = "postingkey";
-    
-    private void parseTag(String tagName, Map attr, String body, EventReceiver receiver) {
-        tagName = tagName.toLowerCase();
-        if (T_BOLD.equals(tagName)) {
-            receiver.receiveBold(body);
-        } else if (T_ITALIC.equals(tagName)) {
-            receiver.receiveItalic(body);
-        } else if (T_UNDERLINE.equals(tagName)) {
-            receiver.receiveUnderline(body);
-        } else if (T_CUT.equals(tagName)) {
-            receiver.receiveCut(body);
-        } else if (T_IMAGE.equals(tagName)) {
-            receiver.receiveImage(body, getInt(P_ATTACHMENT, attr));
-        } else if (T_QUOTE.equals(tagName)) {
-            receiver.receiveQuote(body, getString(P_WHO_QUOTED, attr), getSchema(P_QUOTE_LOCATION, attr), getLocation(P_QUOTE_LOCATION, attr));
-        } else if (T_CODE.equals(tagName)) {
-            receiver.receiveCode(body, getSchema(P_CODE_LOCATION, attr), getLocation(P_CODE_LOCATION, attr));
-        } else if (T_BLOG.equals(tagName)) {
-            List locations = new ArrayList();
-            int i = 0;
-            while (true) {
-                String s = getString("archive" + i, attr);
-                if (s != null)
-                    locations.add(new SafeURL(s));
-                else
-                    break;
-                i++;
-            }
-            receiver.receiveBlog(getString(P_BLOG_NAME, attr), getString(P_BLOG_HASH, attr), getString(P_BLOG_TAG, attr), 
-                                 getLong(P_BLOG_ENTRY, attr), locations, body);
-        } else if (T_ARCHIVE.equals(tagName)) {
-            receiver.receiveArchive(getString(P_ARCHIVE_NAME, attr), getString(P_ARCHIVE_DESCRIPTION, attr), 
-                                    getString(P_ARCHIVE_LOCATION_SCHEMA, attr), getString(P_ARCHIVE_LOCATION, attr), 
-                                    getString(P_ARCHIVE_POSTING_KEY, attr), body);
-        } else if (T_LINK.equals(tagName)) {
-            receiver.receiveLink(getString(P_LINK_SCHEMA, attr), getString(P_LINK_LOCATION, attr), body);
-        } else if (T_ADDRESS.equals(tagName)) {
-            receiver.receiveAddress(getString(P_ADDRESS_NAME, attr), getString(P_ADDRESS_SCHEMA, attr), getString(P_ADDRESS_PROTOCOL, attr), getString(P_ADDRESS_LOCATION, attr), body);
-        } else if (T_H1.equals(tagName)) {
-            receiver.receiveH1(body);
-        } else if (T_H2.equals(tagName)) {
-            receiver.receiveH2(body);
-        } else if (T_H3.equals(tagName)) {
-            receiver.receiveH3(body);
-        } else if (T_H4.equals(tagName)) {
-            receiver.receiveH4(body);
-        } else if (T_H5.equals(tagName)) {
-            receiver.receiveH5(body);
-        } else if (T_HR.equals(tagName)) {
-            receiver.receiveHR();
-        } else if (T_PRE.equals(tagName)) {
-            receiver.receivePre(body);
-        } else if (T_ATTACHMENT.equals(tagName)) {
-            receiver.receiveAttachment(
-                            (int)getLong(P_ATTACHMENT_ID, attr),
-                            (int)getLong(P_THUMBNAIL, attr),
-                            body);
-        } else {
-            if (_log.shouldLog(Log.WARN))
-                _log.warn("need to learn how to parse the tag [" + tagName + "]");
-        }
-    }
-    
-    private String getString(String param, Map attributes) { return (String)attributes.get(param); }
-    private String getSchema(String param, Map attributes) {
-        String url = getString(param, attributes);
-        if (url != null) {
-            SafeURL u = new SafeURL(url);
-            return u.getSchema();
-        } else {
-            return null;
-        }
-    }
-    
-    private String getLocation(String param, Map attributes) {
-        String url = getString(param, attributes);
-        if (url != null) {
-            SafeURL u = new SafeURL(url);
-            return u.getLocation();
-        } else {
-            return null;
-        }
-    }
-    
-    private int getInt(String attributeName, Map attributes) {
-        String val = (String)attributes.get(attributeName.toLowerCase());
-        if (val != null) {
-            try {
-                return Integer.parseInt(val.trim());
-            } catch (NumberFormatException nfe) {
-                //nfe.printStackTrace();
-                return -1;
-            }
-        } else {
-            return -1;
-        }
-    }
-    
-    private long getLong(String attributeName, Map attributes) {
-        String val = (String)attributes.get(attributeName.toLowerCase());
-        if (val != null) {
-            try {
-                return Long.parseLong(val.trim());
-            } catch (NumberFormatException nfe) {
-                //nfe.printStackTrace();
-                return -1;
-            }
-        } else {
-            return -1;
-        }
-    }
-    
-    private String getTagName(String source, int nameStart) {
-        int off = nameStart;
-        while (true) {
-            char c = source.charAt(off);
-            if ( (c == TAG_END) || (WHITESPACE.indexOf(c) >= 0) )
-                return source.substring(nameStart, off);
-            off++;
-        }
-    }
-    private Map getAttributes(String source, int attributesStart, int openTagEnd) {
-        Map rv = new HashMap();
-        int off = attributesStart;
-        int nameStart = -1;
-        int nameEnd = -1;
-        int valStart = -1;
-        int valEnd = -1;
-        while (true) {
-            char c = source.charAt(off);
-            if ( (c == TAG_END) || (off >= openTagEnd) )
-                break;
-            if (WHITESPACE.indexOf(c) < 0) {
-                if (nameStart < 0) {
-                    nameStart = off;
-                } else if (c == EQ) {
-                    if (nameEnd < 0)
-                        nameEnd = off;
-                } else if ( c == DQUOTE ) {
-                    if (valStart < 0) {
-                        valStart = off;
-                    } else {
-                        valEnd = off;
-
-                        if ( ( nameStart >= 0 ) && 
-                            ( nameEnd >= 0 ) && 
-                            ( valStart >= 0 ) && 
-                            ( valEnd >= 0 )) {
-                            String name = source.substring(nameStart, nameEnd);
-                            String val = source.substring(valStart+1, valEnd);
-                            rv.put(name.trim(), val.trim());
-                        }
-                        nameStart = -1;
-                        nameEnd = -1;
-                        valStart = -1;
-                        valEnd = -1;
-                    }
-                }
-            }
-            off++;
-        }
-        return rv;
-    }
-    
-    public interface EventReceiver {
-        public void receiveHeader(String header, String value);
-        public void receiveLink(String schema, String location, String text);
-        /** @param blogArchiveLocations list of SafeURL */
-        public void receiveBlog(String name, String blogKeyHash, String blogPath, long blogEntryId, 
-                                List blogArchiveLocations, String anchorText);
-        public void receiveArchive(String name, String description, String locationSchema, String location, 
-                                   String postingKey, String anchorText);
-        public void receiveImage(String alternateText, int attachmentId);
-        public void receiveAddress(String name, String schema, String protocol, String location, String anchorText);
-        public void receiveAttachment(int id, int thumb, String anchorText);
-        public void receiveBold(String text);
-        public void receiveItalic(String text);
-        public void receiveUnderline(String text);
-        public void receiveH1(String text);
-        public void receiveH2(String text);
-        public void receiveH3(String text);
-        public void receiveH4(String text);
-        public void receiveH5(String text);
-        public void receivePre(String text);
-        public void receiveHR();
-        public void receiveQuote(String text, String whoQuoted, String quoteLocationSchema, String quoteLocation);
-        public void receiveCode(String text, String codeLocationSchema, String codeLocation);
-        public void receiveCut(String summaryText);
-        public void receivePlain(String text);
-        public void receiveNewline();
-        public void receiveLT();
-        public void receiveGT();
-        public void receiveLeftBracket();
-        public void receiveRightBracket();
-        public void receiveBegin();
-        public void receiveEnd();
-        public void receiveHeaderEnd();
-    }
-    
-    public static void main(String args[]) {
-        test(null);
-        test("");
-        test("A: B");
-        test("A: B\n");
-        test("A: B\nC: D");
-        test("A: B\nC: D\n");
-        test("A: B\nC: D\n\n");
-        
-        test("A: B\nC: D\n\nblah");
-        test("A: B\nC: D\n\nblah[[");
-        test("A: B\nC: D\n\nblah]]");
-        test("A: B\nC: D\n\nblah]]blah");
-        test("A: B\nC: D\n\nfoo[a]b[/a]bar");
-        test("A: B\nC: D\n\nfoo[a]b[/a]bar[b][/b]");
-        test("A: B\nC: D\n\nfoo[a]b[/a]bar[b][/b]baz");
-        
-        test("A: B\nC: D\n\n<a href=\"http://odci.gov\">hi</a>");
-        
-        test("A: B\n\n[a b=\"c\"]d[/a]");
-        test("A: B\n\n[a b=\"c\" d=\"e\" f=\"g\"]h[/a]");
-        test("A: B\n\n[a b=\"c\" d=\"e\" f=\"g\"]h[/a][a b=\"c\" d=\"e\" f=\"g\"]h[/a][a b=\"c\" d=\"e\" f=\"g\"]h[/a]");
-        
-        test("A: B\n\n[a   b=\"plural c's\" ]d[/a]");
-        test("A: B\n\n[a   b=\"c\" ]d[/a]");
-        
-        test("A: B\n\n[b]This[/b] is [i]special[/i][cut]why?[/cut][u]because I say so[/u].\neven if you dont care");
-        test("A: B\n\nHi\n[pre]>foo&bar<>blah!blah\nblah\nblah[/pre]foo![pre]bar[/pre]");
-        //(openTagEnd seems wrong) test("A: B\n\n[link schema=\"web\" location=\"http://w.i2p?i2paddr...\"] Try it [[i2p]] [/link]");
-    }
-    private static void test(String rawSML) {
-        I2PAppContext ctx = I2PAppContext.getGlobalContext();
-        SMLParser parser = new SMLParser(ctx);
-        parser.parse(rawSML, new EventReceiverImpl(ctx));
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/ThreadedHTMLRenderer.java b/apps/syndie/java/src/net/i2p/syndie/sml/ThreadedHTMLRenderer.java
deleted file mode 100644
index 70137b6f6228ba1efc7a0e7ad44c48e99e6af651..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/sml/ThreadedHTMLRenderer.java
+++ /dev/null
@@ -1,601 +0,0 @@
-package net.i2p.syndie.sml;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-
-import net.i2p.I2PAppContext;
-import net.i2p.client.naming.PetName;
-import net.i2p.data.Base64;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.Attachment;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.syndie.data.SafeURL;
-import net.i2p.syndie.data.ThreadIndex;
-import net.i2p.syndie.data.ThreadNode;
-import net.i2p.syndie.web.AddressesServlet;
-import net.i2p.syndie.web.ArchiveViewerBean;
-import net.i2p.syndie.web.BaseServlet;
-import net.i2p.syndie.web.PostServlet;
-import net.i2p.util.Log;
-
-/**
- *
- */
-public class ThreadedHTMLRenderer extends HTMLRenderer {
-    private Log _log;
-    private String _baseURI;
-    private boolean _inlineReply;
-    
-    public ThreadedHTMLRenderer(I2PAppContext ctx) {
-        super(ctx);
-        _log = ctx.logManager().getLog(ThreadedHTMLRenderer.class);
-    }
-    
-    /** what, if any, post should be rendered */
-    public static final String PARAM_VIEW_POST = "post";
-    /** what, if any, thread should be rendered in its entirety */
-    public static final String PARAM_VIEW_THREAD = "thread";
-    /** what post should be visible in the nav tree */
-    public static final String PARAM_VISIBLE = "visible";
-    public static final String PARAM_ADD_TO_GROUP_LOCATION = "addLocation";
-    public static final String PARAM_ADD_TO_GROUP_NAME = "addGroup";
-    /** name of the bookmarked entry to remove */
-    public static final String PARAM_REMOVE_FROM_GROUP_NAME = "removeName";
-    /** group to remove from the bookmarked entry, or if blank, remove the entry itself */
-    public static final String PARAM_REMOVE_FROM_GROUP = "removeGroup";
-    /** add the specified tag to the favorites list */
-    public static final String PARAM_ADD_TAG = "addTag";
-    /** index into the nav tree to start displaying */
-    public static final String PARAM_OFFSET = "offset";
-    public static final String PARAM_TAGS = "tags";
-    /** only show threads that the given author participates in */
-    public static final String PARAM_AUTHOR = "author";
-    /** only show threads started by the given author */
-    public static final String PARAM_THREAD_AUTHOR = "threadAuthorOnly";
-    /** search back through the blog for entries this many days */
-    public static final String PARAM_DAYS_BACK = "daysBack";
-    // parameters for editing one's profile
-    public static final String PARAM_PROFILE_NAME = "profileName";
-    public static final String PARAM_PROFILE_DESC = "profileDesc";
-    public static final String PARAM_PROFILE_URL = "profileURL";
-    public static final String PARAM_PROFILE_OTHER = "profileOther";
-    
-    public static String getFilterByTagLink(String uri, ThreadNode node, User user, String tag, String author) { 
-        StringBuffer buf = new StringBuffer(64);
-        buf.append(uri).append('?');
-        if (node != null) {
-            buf.append(PARAM_VIEW_POST).append('=');
-            buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
-            buf.append(node.getEntry().getEntryId()).append('&');
-        }
-        
-        if (!empty(tag))
-            buf.append(PARAM_TAGS).append('=').append(tag).append('&');
-        
-        if (!empty(author))
-            buf.append(PARAM_AUTHOR).append('=').append(author).append('&');
-        
-        return buf.toString();
-    }
-    
-    public static String getAddTagToFavoritesLink(String uri, String tag, String author, String visible, String viewPost, 
-                                                  String viewThread, String offset) {   
-    //protected String getAddToGroupLink(User user, Hash author, String group, String uri, String visible,
-    //                                 String viewPost, String viewThread, String offset, String tags, String filteredAuthor) {
-        StringBuffer buf = new StringBuffer(64);
-        buf.append(uri);
-        buf.append('?');
-        if (!empty(visible))
-            buf.append(PARAM_VISIBLE).append('=').append(visible).append('&');
-        buf.append(PARAM_ADD_TAG).append('=').append(sanitizeTagParam(tag)).append('&');
-
-        if (!empty(viewPost))
-            buf.append(PARAM_VIEW_POST).append('=').append(viewPost).append('&');
-        else if (!empty(viewThread))
-            buf.append(PARAM_VIEW_THREAD).append('=').append(viewThread).append('&');
-        
-        if (!empty(offset))
-            buf.append(PARAM_OFFSET).append('=').append(offset).append('&');
-
-        if (!empty(author))
-            buf.append(PARAM_AUTHOR).append('=').append(author).append('&');
-        
-        BaseServlet.addAuthActionParams(buf);
-        return buf.toString();
-    }
-    
-    public static String getNavLink(String uri, String viewPost, String viewThread, String tags, String author, boolean authorOnly, int offset) {
-        StringBuffer buf = new StringBuffer(64);
-        buf.append(uri);
-        buf.append('?');
-        if (!empty(viewPost))
-            buf.append(PARAM_VIEW_POST).append('=').append(viewPost).append('&');
-        else if (!empty(viewThread))
-            buf.append(PARAM_VIEW_THREAD).append('=').append(viewThread).append('&');
-        
-        if (!empty(tags))
-            buf.append(PARAM_TAGS).append('=').append(tags).append('&');
-        
-        if (!empty(author)) {
-            buf.append(PARAM_AUTHOR).append('=').append(author).append('&');
-            if (authorOnly)
-                buf.append(PARAM_THREAD_AUTHOR).append("=true&");
-        }
-        
-        buf.append(PARAM_OFFSET).append('=').append(offset).append('&');
-        
-        return buf.toString();
-    }
-    
-    public static String getViewPostLink(String uri, ThreadNode node, User user, boolean isPermalink, 
-                                         String offset, String tags, String author, boolean authorOnly) {
-        if (isPermalink) {
-            // link to the blog view of the original poster
-            BlogURI rootBlog = null;
-            ThreadNode parent = node;
-            while (parent != null) {
-                if (parent.getParent() != null) {
-                    parent = parent.getParent();
-                } else {
-                    rootBlog = parent.getEntry();
-                    break;
-                }
-            }
-            BlogInfo root = BlogManager.instance().getArchive().getBlogInfo(rootBlog.getKeyHash());
-            return BlogRenderer.getEntryURL(parent.getEntry(), root, node.getEntry(), true);
-        } else {
-            StringBuffer buf = new StringBuffer(64);
-            buf.append(uri);
-            if (node.getChildCount() > 0) {
-                buf.append('?').append(PARAM_VISIBLE).append('=');
-                ThreadNode child = node.getChild(0);
-                buf.append(child.getEntry().getKeyHash().toBase64()).append('/');
-                buf.append(child.getEntry().getEntryId()).append('&');
-            } else {
-                buf.append('?').append(PARAM_VISIBLE).append('=');
-                buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
-                buf.append(node.getEntry().getEntryId()).append('&');
-            }
-            buf.append(PARAM_VIEW_POST).append('=');
-            buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
-            buf.append(node.getEntry().getEntryId()).append('&');
-
-            if (!isPermalink) {
-                if (!empty(offset))
-                    buf.append(PARAM_OFFSET).append('=').append(offset).append('&');
-                if (!empty(tags))
-                    buf.append(PARAM_TAGS).append('=').append(tags).append('&');
-            }
-
-            if (authorOnly && !empty(author)) {
-                buf.append(PARAM_AUTHOR).append('=').append(author).append('&');
-                buf.append(PARAM_THREAD_AUTHOR).append("=true&");
-            } else if (!isPermalink && !empty(author))
-                buf.append(PARAM_AUTHOR).append('=').append(author).append('&');
-        
-            return buf.toString();
-        }
-    }
-    
-    public static String getViewPostLink(String uri, BlogURI post, User user, boolean isPermalink, 
-                                         String offset, String tags, String author, boolean authorOnly) {
-        StringBuffer buf = new StringBuffer(64);
-        buf.append(uri);
-        buf.append('?').append(PARAM_VISIBLE).append('=');
-        buf.append(post.getKeyHash().toBase64()).append('/');
-        buf.append(post.getEntryId()).append('&');
-        buf.append(PARAM_VIEW_POST).append('=');
-        buf.append(post.getKeyHash().toBase64()).append('/');
-        buf.append(post.getEntryId()).append('&');
-        
-        if (!isPermalink) {
-            if (!empty(offset))
-                buf.append(PARAM_OFFSET).append('=').append(offset).append('&');
-            if (!empty(tags))
-                buf.append(PARAM_TAGS).append('=').append(tags).append('&');
-            if (!empty(author)) {
-                buf.append(PARAM_AUTHOR).append('=').append(author).append('&');
-                if (authorOnly)
-                    buf.append(PARAM_THREAD_AUTHOR).append("=true&");
-            }
-        }
-        
-        return buf.toString();
-    }
-    
-    private static final boolean empty(String val) { return (val == null) || (val.trim().length() <= 0); }
-    
-    /**
-     * @param replyHiddenFields HTML of hidden input fields necessary for the reply form to be honored
-     */
-    public void render(User user, Writer out, Archive archive, BlogURI post, 
-                       boolean inlineReply, ThreadIndex index, String baseURI, String replyHiddenFields,
-                       String offset, String requestTags, String filteredAuthor, boolean authorOnly) throws IOException {
-        EntryContainer entry = archive.getEntry(post);
-        if (entry == null) return;
-        ThreadNode node = index.getNode(post);
-        if (node == null) {
-            _log.error("Post is not in the index: " + post.toString());
-            return;
-        }
-        _entry = entry;
-   
-        _baseURI = baseURI;
-        _user = user;
-        _out = out;
-        _archive = archive;
-        _cutBody = false;
-        _showImages = true;
-        _inlineReply = inlineReply;
-        _headers = new HashMap();
-        _bodyBuffer = new StringBuffer(1024);
-        _postBodyBuffer = new StringBuffer(1024);
-        _addresses = new ArrayList();
-        _links = new ArrayList();
-        _blogs = new ArrayList();
-        _archives = new ArrayList();
-        
-        _parser.parse(entry.getEntry().getText(), this);
-        
-        out.write("<!-- body begin -->\n");
-        out.write("<!-- body meta begin -->\n");
-        out.write("<tr class=\"postMeta\" id=\"" + post.toString() + "\">\n");
-        
-        String subject = (String)_headers.get(HTMLRenderer.HEADER_SUBJECT);
-        if (subject == null)
-            subject = "";
-        out.write(" <td colspan=\"3\" class=\"postMetaSubject\" id=\"bodySubject\">");
-        out.write(subject);
-        out.write("</td></tr>\n");
-        out.write("<tr class=\"postMeta\"><td colspan=\"3\" class=\"postMetaLink\">\n");
-        out.write("<a href=\"");
-        out.write(getMetadataURL(post.getKeyHash()));
-        out.write("\" title=\"View the author's profile\">");
-        
-        String author = null;
-        PetName pn = user.getPetNameDB().getByLocation(post.getKeyHash().toBase64());
-        if (pn == null) {
-            BlogInfo info = archive.getBlogInfo(post.getKeyHash());
-            if (info != null)
-                author = info.getProperty(BlogInfo.NAME);
-        } else {
-            author = pn.getName();
-        }
-        if ( (author == null) || (author.trim().length() <= 0) )
-            author = post.getKeyHash().toBase64().substring(0,6);
-        
-        out.write(author);
-        out.write("</a> @ ");
-        out.write(getEntryDate(post.getEntryId()));
-        
-        Collection tags = node.getTags();
-        if ( (tags != null) && (tags.size() > 0) ) {
-            out.write("\nTags: \n");
-            for (Iterator tagIter = tags.iterator(); tagIter.hasNext(); ) {
-                String tag = (String)tagIter.next();
-                out.write("<a href=\"");
-                out.write(getFilterByTagLink(baseURI, node, user, tag, filteredAuthor));
-                out.write("\" title=\"Filter threads to only include posts tagged as '");
-                out.write(tag);
-                out.write("'\">");
-                out.write(" " + tag);
-                out.write("</a>\n");
-                if (user.getAuthenticated() && (!user.getFavoriteTags().contains(tag)) && (!"[none]".equals(tag)) ) {
-                    out.write("<a href=\"");
-                    String cur = node.getEntry().getKeyHash().toBase64() + '/' + node.getEntry().getEntryId();
-                    out.write(getAddTagToFavoritesLink(baseURI, tag, filteredAuthor, cur, null, cur, offset));
-                    out.write("\" title=\"Add the tag '");
-                    out.write(tag);
-                    out.write("' to your favorites list\">");
-                    out.write("<img src=\"images/addToFavorites.png\" alt=\":)\" border=\"0\" />");
-                    out.write("</a>\n");
-                }
-            }
-        }
-        
-        out.write("\n<a href=\"");
-        out.write(getViewPostLink(baseURI, node, user, true, offset, requestTags, filteredAuthor, authorOnly));
-        out.write("\" title=\"Select a link directly to this post within the blog\">permalink</a>\n");
-
-        if (true || (!inlineReply) ) {
-            String refuseReply = (String)_headers.get(HEADER_REFUSE_REPLIES);
-            boolean allowReply = false;
-            if ( (refuseReply != null) && (Boolean.valueOf(refuseReply).booleanValue()) ) {
-                if (_entry == null ) 
-                    allowReply = false;
-                else if ( (_user == null) || (_user.getBlog() == null) )
-                    allowReply = false;
-                else if (_entry.getURI().getKeyHash().equals(_user.getBlog()))
-                    allowReply = true;
-                else
-                    allowReply = false;
-            } else {
-                allowReply = true;
-            }
-            if (allowReply && (_entry != null) ) {
-                out.write("<a href=\"post.jsp?");
-                out.write(PostServlet.PARAM_PARENT + '=' + 
-                          Base64.encode(_entry.getURI().getKeyHash().toBase64() + '/' + _entry.getURI().getEntryId()));
-                out.write("\" title=\"Reply to the current post\" >Reply</a><br />\n");
-            }
-        }
-        
-        out.write("</td>\n</tr>\n");
-        out.write("<!-- body meta end -->\n");
-        out.write("<!-- body post begin -->\n");
-        out.write("<tr class=\"postData\">\n");
-        out.write("<td colspan=\"3\">\n");
-        out.write(_bodyBuffer.toString());
-        out.write("</td>\n</tr>\n");
-        out.write("<!-- body post end -->\n");
-        out.write("<!-- body details begin -->\n");
-        out.write(_postBodyBuffer.toString());
-/*
-"<tr class=\"postDetails\">\n" +
-" <form action=\"viewattachment.jsp\" method=\"GET\">\n" +
-" <td colspan=\"3\">\n" +
-" External links:\n" +
-"  <a href=\"external.jsp?foo\" title=\"View foo.i2p\">http://foo.i2p/</a>\n" +
-"  <a href=\"external.jsp?bar\" title=\"View bar.i2p\">http://bar.i2p/</a>\n" +
-" <br />\n" +
-" Attachments: <select name=\"attachment\">\n" +
-"  <option value=\"0\">sampleRawSML.sml: Sample SML file with headers (4KB, type text/plain)</option>\n" +
-" </select> <input type=\"submit\" name=\"action\" value=\"Download\" />\n" +
-" <br /><a href=\"\" title=\"Expand the entire thread\">Full thread</a>\n" +
-" <a href=\"\" title=\"Previous post in the thread\">Prev in thread</a> \n" +
-" <a href=\"\" title=\"Next post in the thread\">Next in thread</a> \n" +
-" </td>\n" +
-" </form>\n" +
-"</tr>\n" +
- */
-        out.write("<!-- body details end -->\n");
-        if (inlineReply && user.getAuthenticated() ) {
-            String refuseReplies = (String)_headers.get(HTMLRenderer.HEADER_REFUSE_REPLIES);
-            // show the reply form if we are the author or replies have not been explicitly rejected
-            if ( (user.getBlog().equals(post.getKeyHash())) ||
-                 (refuseReplies == null) || (!Boolean.valueOf(refuseReplies).booleanValue()) ) {
-                out.write("<!-- body reply begin -->\n");
-                out.write("<form action=\"post.jsp\" method=\"POST\" enctype=\"multipart/form-data\">\n");
-                out.write(replyHiddenFields);
-                out.write("<input type=\"hidden\" name=\"" + PostServlet.PARAM_PARENT + "\" value=\"");
-                out.write(Base64.encode(post.toString()));
-                out.write("\" />");
-                out.write("<input type=\"hidden\" name=\"" + PostServlet.PARAM_SUBJECT + "\" value=\"");
-                if (subject.indexOf("re: ") == -1)
-                    out.write("re: ");
-                out.write(HTMLRenderer.sanitizeTagParam(subject));
-                out.write("\" />");
-                out.write("<tr class=\"postReply\">\n");
-                out.write("<td colspan=\"3\">Reply: (<a href=\"smlref.jsp\" title=\"SML cheatsheet\" target=\"_blank\">SML reference</a>)</td>\n</tr>\n");
-                out.write("<tr class=\"postReplyText\">\n");
-                out.write("<td colspan=\"3\"><textarea name=\"" + PostServlet.PARAM_TEXT + "\" rows=\"2\" cols=\"100\"></textarea></td>\n");
-                out.write("</tr>\n");
-                out.write("<tr class=\"postReplyOptions\">\n");
-                out.write(" <td colspan=\"3\">\n");
-                out.write(" <input type=\"submit\" value=\"Preview...\" name=\"Post\" />\n");
-                out.write(" Tags: ");
-                BaseServlet.writeTagField(_user, "", out, "Optional tags to categorize your response", "No tags", false);
-                // <input type=\"text\" size=\"10\" name=\"" + PostServlet.PARAM_TAGS + "\" title=\"Optional tags to categorize your response\" />\n");
-                out.write(" in a new thread? <input type=\"checkbox\" name=\"" + PostServlet.PARAM_IN_NEW_THREAD + "\" value=\"true\" title=\"If true, this will fork a new top level thread\" />\n");
-                out.write(" refuse replies? <input type=\"checkbox\" name=\"" + PostServlet.PARAM_REFUSE_REPLIES + "\" value=\"true\" title=\"If true, only you will be able to reply to the post\" />\n");
-                out.write(" attachment: <input type=\"file\" name=\"entryfile0\" />\n");
-                out.write(" </td>\n</tr>\n</form>\n");
-                out.write("<!-- body reply end -->\n");
-            }
-        }
-        out.write("<!-- body end -->\n");
-    }
-    
-    public void receiveEnd() { 
-        _postBodyBuffer.append("<tr class=\"postDetails\">\n");
-        _postBodyBuffer.append(" <form action=\"viewattachment.jsp\" method=\"GET\">\n");
-        _postBodyBuffer.append(" <td colspan=\"3\" valign=\"top\" align=\"left\">\n");
-        
-        _postBodyBuffer.append("<input type=\"hidden\" name=\"").append(ArchiveViewerBean.PARAM_BLOG);
-        _postBodyBuffer.append("\" value=\"");
-        if (_entry != null)
-            _postBodyBuffer.append(Base64.encode(_entry.getURI().getKeyHash().getData()));
-        else
-            _postBodyBuffer.append("unknown");
-        _postBodyBuffer.append("\" />\n");
-        _postBodyBuffer.append("<input type=\"hidden\" name=\"").append(ArchiveViewerBean.PARAM_ENTRY);
-        _postBodyBuffer.append("\" value=\"");
-        if (_entry != null) 
-            _postBodyBuffer.append(_entry.getURI().getEntryId());
-        else
-            _postBodyBuffer.append("unknown");
-        _postBodyBuffer.append("\" />\n");
-        
-        //_postBodyBuffer.append("<td colspan=\"2\" valign=\"top\" align=\"left\" ").append(getClass("summDetail")).append(" >\n");
-
-        if ( (_entry != null) && (_entry.getAttachments() != null) && (_entry.getAttachments().length > 0) ) {
-            _postBodyBuffer.append(getSpan("summDetailAttachment")).append("Attachments:</span> ");
-            _postBodyBuffer.append("<select ").append(getClass("summDetailAttachmentId")).append(" name=\"").append(ArchiveViewerBean.PARAM_ATTACHMENT).append("\">\n");
-            for (int i = 0; i < _entry.getAttachments().length; i++) {
-                _postBodyBuffer.append("<option value=\"").append(i).append("\">");
-                Attachment a = _entry.getAttachments()[i];
-                _postBodyBuffer.append(sanitizeString(a.getName(), 30));
-                if ( (a.getDescription() != null) && (a.getDescription().trim().length() > 0) ) {
-                    _postBodyBuffer.append(": ");
-                    _postBodyBuffer.append(sanitizeString(a.getDescription(), 30));
-                }
-                _postBodyBuffer.append(" (").append(a.getDataLength()/1024).append("KB");
-                _postBodyBuffer.append(", type ").append(sanitizeString(a.getMimeType())).append(")</option>\n");
-            }
-            _postBodyBuffer.append("</select>\n");
-            _postBodyBuffer.append("<input ").append(getClass("summDetailAttachmentDl")).append(" type=\"submit\" value=\"Download\" name=\"Download\" /><br />\n");
-        }
-
-        if (_blogs.size() > 0) {
-            _postBodyBuffer.append(getSpan("summDetailBlog")).append("Blog references:</span>");
-            for (int i = 0; i < _blogs.size(); i++) {
-                Blog b = (Blog)_blogs.get(i);
-                _postBodyBuffer.append("<a ").append(getClass("summDetailBlogLink")).append(" href=\"");
-                boolean expanded = (_user != null ? _user.getShowExpanded() : false);
-                boolean images = (_user != null ? _user.getShowImages() : false);
-                _postBodyBuffer.append(getPageURL(new Hash(Base64.decode(b.hash)), b.tag, b.entryId, -1, -1, expanded, images));
-                _postBodyBuffer.append("\">").append(sanitizeString(b.name, 30)).append("</a> ");
-            }
-            _postBodyBuffer.append("<br />\n");
-        }
-
-        if (_links.size() > 0) {
-            _postBodyBuffer.append(getSpan("summDetailExternal")).append("External links:</span> ");
-            for (int i = 0; i < _links.size(); i++) {
-                Link l = (Link)_links.get(i);
-                String schema = l.schema;
-                _postBodyBuffer.append("<a ");
-                _postBodyBuffer.append(getClass("summDetailExternalLink")).append(" href=\"externallink.jsp?");
-                if (l.schema != null)
-                        _postBodyBuffer.append("schema=").append(sanitizeURL(l.schema)).append('&');
-                if (l.location != null)
-                        _postBodyBuffer.append("location=").append(sanitizeURL(l.location)).append('&');
-                _postBodyBuffer.append("\" title=\"Goto ").append(sanitizeTagParam(l.location));
-                _postBodyBuffer.append("\">").append(sanitizeString(l.location, 30));
-                _postBodyBuffer.append(getSpan("summDetailExternalNet")).append(" (").append(sanitizeString(l.schema)).append(")</span></a> ");
-            }
-            _postBodyBuffer.append("<br />\n");
-        }
-
-        if (_addresses.size() > 0) {
-            _postBodyBuffer.append(getSpan("summDetailAddr")).append("Addresses:</span>");
-            for (int i = 0; i < _addresses.size(); i++) {
-                Address a = (Address)_addresses.get(i);
-                importAddress(a);
-                PetName pn = null;
-                if (_user != null)
-                    pn = _user.getPetNameDB().getByLocation(a.location);
-                if (pn != null) {
-                    _postBodyBuffer.append(' ').append(getSpan("summDetailAddrKnown"));
-                    _postBodyBuffer.append(sanitizeString(pn.getName())).append("</span>");
-                } else {
-                    _postBodyBuffer.append(" <a ").append(getClass("summDetailAddrLink")).append(" href=\"addresses.jsp?");
-                    if (a.schema != null)
-                        _postBodyBuffer.append(AddressesServlet.PARAM_NET).append("=").append(sanitizeTagParam(a.schema)).append('&');
-                    if (a.location != null)
-                        _postBodyBuffer.append(AddressesServlet.PARAM_LOC).append("=").append(sanitizeTagParam(a.location)).append('&');
-                    if (a.name != null)
-                        _postBodyBuffer.append(AddressesServlet.PARAM_NAME).append("=").append(sanitizeTagParam(a.name)).append('&');
-                    if (a.protocol != null)
-                        _postBodyBuffer.append(AddressesServlet.PARAM_PROTO).append("=").append(sanitizeTagParam(a.protocol)).append('&');
-                    _postBodyBuffer.append("\">").append(sanitizeString(a.name, 30)).append("</a>");
-                }                    
-            }
-            _postBodyBuffer.append("<br />\n");
-        }
-
-        if (_archives.size() > 0) {
-            _postBodyBuffer.append(getSpan("summDetailArchive")).append("Archives:</span>");
-            for (int i = 0; i < _archives.size(); i++) {
-                ArchiveRef a = (ArchiveRef)_archives.get(i);
-                _postBodyBuffer.append(" <a ").append(getClass("summDetailArchiveLink")).append(" href=\"").append(getArchiveURL(null, new SafeURL(a.locationSchema + "://" + a.location)));
-                _postBodyBuffer.append("\">").append(sanitizeString(a.name)).append("</a>");
-                if (a.description != null)
-                    _postBodyBuffer.append(": ").append(getSpan("summDetailArchiveDesc")).append(sanitizeString(a.description)).append("</span>");
-                if (null == _user.getPetNameDB().getByLocation(a.location)) {
-                    _postBodyBuffer.append(" <a ").append(getClass("summDetailArchiveBookmark")).append(" href=\"");
-                    _postBodyBuffer.append(getBookmarkURL(sanitizeTagParam(a.name), sanitizeTagParam(a.location), sanitizeTagParam(a.locationSchema), AddressesServlet.PROTO_ARCHIVE));
-                    _postBodyBuffer.append("\">bookmark it</a>");
-                }
-            }
-            _postBodyBuffer.append("<br />\n");
-        }
-
-        if (_entry != null) {
-            List replies = _archive.getIndex().getReplies(_entry.getURI());
-            if ( (replies != null) && (replies.size() > 0) ) {
-                _postBodyBuffer.append(getSpan("summDetailReplies")).append("Replies:</span> ");
-                for (int i = 0; i < replies.size(); i++) { 
-                    BlogURI reply = (BlogURI)replies.get(i);
-                    _postBodyBuffer.append("<a ").append(getClass("summDetailReplyLink")).append(" href=\"");
-                    _postBodyBuffer.append(getPageURL(reply.getKeyHash(), null, reply.getEntryId(), -1, -1, true, _user.getShowImages()));
-                    _postBodyBuffer.append("\">");
-                    _postBodyBuffer.append(getSpan("summDetailReplyAuthor"));
-                    BlogInfo replyAuthor = _archive.getBlogInfo(reply);
-                    if (replyAuthor != null) {
-                        _postBodyBuffer.append(sanitizeString(replyAuthor.getProperty(BlogInfo.NAME)));
-                    } else {
-                        _postBodyBuffer.append(reply.getKeyHash().toBase64().substring(0,16));
-                    }
-                    _postBodyBuffer.append("</span> on ");
-                    _postBodyBuffer.append(getSpan("summDetailReplyDate"));
-                    _postBodyBuffer.append(getEntryDate(reply.getEntryId()));
-                    _postBodyBuffer.append("</a></span> ");
-                }
-                _postBodyBuffer.append("<br />");
-            }
-        }
-
-        String inReplyTo = (String)_headers.get(HEADER_IN_REPLY_TO);
-        if ( (inReplyTo != null) && (inReplyTo.trim().length() > 0) ) {
-            BlogURI replyURI = new BlogURI(inReplyTo);
-            if (replyURI.getEntryId() > 0) {
-                _postBodyBuffer.append(" <a ").append(getClass("summDetailParent"));
-                _postBodyBuffer.append(" href=\"");
-                _postBodyBuffer.append(getPageURL(replyURI.getKeyHash(), null, replyURI.getEntryId(), 0, 0, true, true));
-                _postBodyBuffer.append("\">(view parent)</a><br />\n");
-            }
-        }
-
-        _postBodyBuffer.append(" </td>\n");
-        _postBodyBuffer.append(" </form>\n");
-        _postBodyBuffer.append("</tr>\n");
-    }
-    
-    public void receiveHeaderEnd() {
-        //_preBodyBuffer.append("<table ").append(getClass("overall")).append(" width=\"100%\" border=\"0\">\n");
-        //renderSubjectCell();
-        //renderMetaCell();
-        //renderPreBodyCell();
-    }
-    
-    public String getMetadataURL(Hash blog) {
-        return buildProfileURL(blog);
-    }
-    public static String buildProfileURL(Hash blog) {
-        if ( (blog != null) && (blog.getData() != null) )
-            return "profile.jsp?" + ThreadedHTMLRenderer.PARAM_AUTHOR + "=" +
-                   Base64.encode(blog.getData());
-        else
-            return "profile.jsp";
-    }
-    protected String getEntryURL() { return getEntryURL(_user != null ? _user.getShowImages() : false); }
-    protected String getEntryURL(boolean showImages) {
-        if (_entry == null) 
-            return _baseURI;
-        else
-            return _baseURI + '?' + PARAM_VIEW_POST + '=' + 
-                   Base64.encode(_entry.getURI().getKeyHash().getData()) + '/' 
-                   + _entry.getURI().getEntryId() + '&';
-    }
-    
-    public String getPageURL(User user, String selector, int numPerPage, int pageNum) { return _baseURI; }
-    
-    public String getPageURL(Hash blog, String tag, long entryId, String group, int numPerPage, int pageNum, boolean expandEntries, boolean showImages) {
-        StringBuffer buf = new StringBuffer(128);
-        buf.append(_baseURI).append('?');
-        String entry = null;
-        if ( (blog != null) && (entryId > 0) ) {
-            entry = blog.toBase64() + '/' + entryId;
-            buf.append(PARAM_VIEW_THREAD).append('=').append(entry).append('&');
-            buf.append(PARAM_VISIBLE).append('=').append(Base64.encode(blog.getData())).append('/').append(entryId).append('&');
-        } else if (blog != null) {
-            buf.append(PARAM_AUTHOR).append('=').append(blog.toBase64()).append('&');
-        }
-        if (tag != null)
-            buf.append(PARAM_TAGS).append('=').append(sanitizeTagParam(tag)).append('&');
-        if ( (blog != null) && (entryId > 0) )
-            buf.append("#blog://").append(entry);
-        return buf.toString();
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/AddressesServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/AddressesServlet.java
deleted file mode 100644
index ff1a49ccc2bf55e14066c3a69e7fce7572ed034a..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/AddressesServlet.java
+++ /dev/null
@@ -1,504 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Iterator;
-import java.util.TreeSet;
-
-import javax.servlet.http.HttpServletRequest;
-
-import net.i2p.client.naming.PetName;
-import net.i2p.client.naming.PetNameDB;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.FilteredThreadIndex;
-import net.i2p.syndie.data.ThreadIndex;
-import net.i2p.syndie.sml.ThreadedHTMLRenderer;
-
-/**
- * Show the user's addressbook
- *
- */
-public class AddressesServlet extends BaseServlet {
-    public static final String PARAM_IS_PUBLIC = "addrPublic";
-    public static final String PARAM_NAME = "addrName";
-    public static final String PARAM_LOC = "addrLoc";
-    public static final String PARAM_FAVORITE = "addrFavorite";
-    public static final String PARAM_IGNORE = "addrIgnore";
-    public static final String PARAM_NET = "addrNet";
-    public static final String PARAM_PROTO = "addrProto";
-    public static final String PARAM_SYNDICATE = "addrSyndicate";
-    public static final String PARAM_TAG = "addrTag";
-    public static final String PARAM_ACTION = "action";
-    
-    public static final String PROTO_BLOG = "syndieblog";
-    public static final String PROTO_ARCHIVE = "syndiearchive";
-    public static final String PROTO_I2PHEX = "i2phex";
-    public static final String PROTO_EEPSITE = "eep";
-    public static final String PROTO_TAG = "syndietag";
-
-    public static final String NET_SYNDIE = "syndie";
-    public static final String NET_I2P = "i2p";
-    public static final String NET_IP = "ip";
-    public static final String NET_FREENET = "freenet";
-    public static final String NET_TOR = "tor";
-
-    public static final String ACTION_DELETE_BLOG = "Delete author";
-    public static final String ACTION_UPDATE_BLOG = "Update author";
-    public static final String ACTION_ADD_BLOG = "Add author";
-    public static final String ACTION_PURGE_AND_BAN_BLOG = "Purge and ban author";
-    
-    public static final String ACTION_DELETE_ARCHIVE = "Delete archive";
-    public static final String ACTION_UPDATE_ARCHIVE = "Update archive";
-    public static final String ACTION_ADD_ARCHIVE = "Add archive";
-    
-    public static final String ACTION_DELETE_PEER = "Delete peer";
-    public static final String ACTION_UPDATE_PEER = "Update peer";
-    public static final String ACTION_ADD_PEER = "Add peer";
-    
-    public static final String ACTION_DELETE_EEPSITE = "Delete eepsite";
-    public static final String ACTION_UPDATE_EEPSITE = "Update eepsite";
-    public static final String ACTION_ADD_EEPSITE = "Add eepsite";
-    
-    public static final String ACTION_DELETE_TAG = "Delete tag";
-    public static final String ACTION_UPDATE_TAG = "Update tag";
-    public static final String ACTION_ADD_TAG = "Add tag";
-    
-    public static final String ACTION_DELETE_OTHER = "Delete address";
-    public static final String ACTION_UPDATE_OTHER = "Update address";
-    public static final String ACTION_ADD_OTHER = "Add other address";
-        
-    protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, 
-                                        int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
-        if (!user.getAuthenticated()) {
-            out.write("<tr><td colspan=\"3\">You must log in to view your addressbook</d></tr>\n");
-        } else {
-            PetNameDB db = user.getPetNameDB();
-            String uri = req.getRequestURI();
-            
-            PetName pn = buildNewName(req, PROTO_BLOG);
-            _log.debug("pn for protoBlog [" + req.getParameter(PARAM_PROTO) + "]: " + pn);
-            renderBlogs(user, db, uri, pn, out);
-            pn = buildNewName(req, PROTO_ARCHIVE);
-            _log.debug("pn for protoArchive [" + req.getParameter(PARAM_PROTO) + "]: " + pn);
-            renderArchives(user, db, uri, pn, out);
-            pn = buildNewName(req, PROTO_TAG);
-            _log.debug("pn for protoTag [" + req.getParameter(PARAM_TAG) + "]: " + pn);
-            renderTags(user, db, uri, pn, out);
-            pn = buildNewName(req, PROTO_I2PHEX);
-            _log.debug("pn for protoPhex [" + req.getParameter(PARAM_PROTO) + "]: " + pn);
-            renderI2Phex(user, db, uri, pn, out);
-            pn = buildNewName(req, PROTO_EEPSITE);
-            _log.debug("pn for protoEep [" + req.getParameter(PARAM_PROTO) + "]: " + pn);
-            renderEepsites(user, db, uri, pn, out);
-            pn = buildNewName(req);
-            _log.debug("pn for proto other [" + req.getParameter(PARAM_PROTO) + "]: " + pn);
-            renderOther(user, db, uri, pn, out);
-        }
-    }
-
-    private void renderBlogs(User user, PetNameDB db, String baseURI, PetName newName, PrintWriter out) throws IOException {
-        TreeSet names = new TreeSet();
-        for (Iterator iter = db.getNames().iterator(); iter.hasNext(); ) {
-            String name = (String)iter.next();
-            PetName pn = db.getByName(name);
-            if (PROTO_BLOG.equals(pn.getProtocol()))
-                names.add(name);
-        }
-        out.write("<tr><td colspan=\"3\"><b>Syndie authors</b></td></tr>\n");
-        for (Iterator iter = names.iterator(); iter.hasNext(); ) {
-            PetName pn = db.getByName((String)iter.next());
-            out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
-            out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_BLOG + "\" />");
-            out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_SYNDIE + "\" />");
-            writeAuthActionFields(out);
-            out.write("<tr><td colspan=\"3\">");
-            out.write("<input type=\"checkbox\" name=\"" + PARAM_IS_PUBLIC + "\" value=\"true\" " + (pn.getIsPublic() ? " checked=\"true\" " : "") 
-                      + " title=\"If checked, this name can be shared with one click when posting\" />\n");
-            out.write("Name: <input type=\"hidden\" name=\"" + PARAM_NAME + "\" value=\"" + pn.getName() 
-                      + "\" title=\"Short, locally unique 'pet name' for the author\" />" + pn.getName() + " ");
-            out.write("Location: <input type=\"text\" name=\"" + PARAM_LOC + "\" size=\"3\" value=\"" + pn.getLocation() 
-                      + "\" title=\"Blog hash for the author\" /> ");
-            if (pn.isMember(FilteredThreadIndex.GROUP_FAVORITE))
-                out.write("Favorite? <input type=\"checkbox\" name=\"" + PARAM_FAVORITE 
-                          + "\" checked=\"true\" value=\"true\" title=\"If true, their posts are highlighted\" /> ");
-            else
-                out.write("Favorite? <input type=\"checkbox\" name=\"" + PARAM_FAVORITE 
-                          + "\" value=\"true\" title=\"If true, their posts are highlighted\" /> ");
-            
-            if (pn.isMember(FilteredThreadIndex.GROUP_IGNORE)) {
-                out.write("Ignored? <input type=\"checkbox\" name=\"" + PARAM_IGNORE 
-                          + "\" checked=\"true\" value=\"true\" title=\"If true, their threads are hidden\" /> ");
-                if (BlogManager.instance().authorizeRemote(user))
-                    out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_PURGE_AND_BAN_BLOG + "\" /> ");
-            } else {
-                out.write("Ignored? <input type=\"checkbox\" name=\"" + PARAM_IGNORE 
-                          + "\" value=\"true\" title=\"If true, their threads are hidden\" /> ");
-                out.write("<a href=\"" + getControlTarget() + "?" + ThreadedHTMLRenderer.PARAM_AUTHOR + '=' 
-                          + pn.getLocation() + "\" title=\"View threads by the given author\">View posts</a> ");
-            }
-            
-            out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_DELETE_BLOG + "\" /> ");
-            out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_UPDATE_BLOG + "\" /> ");
-            out.write("</td></tr>\n");
-            out.write("</form>\n");
-        }
-        
-        out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
-        writeAuthActionFields(out);
-        out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_BLOG + "\" />");
-        out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_SYNDIE + "\" />");
-        out.write("<tr><td colspan=\"3\">");
-        out.write("<input type=\"checkbox\" name=\"" + PARAM_IS_PUBLIC + "\" value=\"true\" " + (newName.getIsPublic() ? " checked=\"true\" " : "") 
-                  + " title=\"If checked, this name can be shared with one click when posting\" />\n");
-        out.write("Name: <input type=\"text\" name=\"" + PARAM_NAME + "\" size=\"10\" value=\"" + newName.getName() 
-                  + "\" title=\"Short, locally unique 'pet name' for the author\" /> ");
-        out.write("Location: <input type=\"text\" name=\"" + PARAM_LOC + "\" size=\"3\" value=\"" + newName.getLocation() 
-                  + "\"  title=\"Blog hash for the author\" /> ");
-        if (newName.isMember(FilteredThreadIndex.GROUP_FAVORITE))
-            out.write("Favorite? <input type=\"checkbox\" name=\"" + PARAM_FAVORITE 
-                      + "\" checked=\"true\" value=\"true\" title=\"If true, their posts are highlighted\" /> ");
-        else
-            out.write("Favorite? <input type=\"checkbox\" name=\"" + PARAM_FAVORITE 
-                      + "\" value=\"true\" title=\"If true, their posts are highlighted\" /> ");
-
-        if (newName.isMember(FilteredThreadIndex.GROUP_IGNORE)) {
-            out.write("Ignored? <input type=\"checkbox\" name=\"" + PARAM_IGNORE 
-                      + "\" checked=\"true\" value=\"true\" title=\"If true, their threads are hidden\" /> ");
-        } else {
-            out.write("Ignored? <input type=\"checkbox\" name=\"" + PARAM_IGNORE 
-                      + "\" value=\"true\" title=\"If true, their threads are hidden\" /> ");
-        }
-
-        out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_ADD_BLOG + "\" /> ");
-        out.write("</td></tr>\n");
-        out.write("</form>\n");
-            
-        out.write("<tr><td colspan=\"3\"><hr /></td></tr>\n");
-    }
-
-    private void renderArchives(User user, PetNameDB db, String baseURI, PetName newName, PrintWriter out) throws IOException {
-        TreeSet names = new TreeSet();
-        for (Iterator iter = db.getNames().iterator(); iter.hasNext(); ) {
-            String name = (String)iter.next();
-            PetName pn = db.getByName(name);
-            if (PROTO_ARCHIVE.equals(pn.getProtocol()))
-                names.add(name);
-        }
-        out.write("<tr><td colspan=\"3\"><b>Syndie archives</b></td></tr>\n");
-        for (Iterator iter = names.iterator(); iter.hasNext(); ) {
-            PetName pn = db.getByName((String)iter.next());
-            out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
-            writeAuthActionFields(out);
-            out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_ARCHIVE + "\" />");
-            out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_SYNDIE + "\" />");
-            out.write("<tr><td colspan=\"3\">");
-            out.write("<input type=\"checkbox\" name=\"" + PARAM_IS_PUBLIC + "\" value=\"true\" " + (pn.getIsPublic() ? " checked=\"true\" " : "") 
-                      + " title=\"If checked, this name can be shared with one click when posting\" />\n");
-            out.write("Name: <input type=\"hidden\" name=\"" + PARAM_NAME + "\" size=\"10\" value=\"" + pn.getName() 
-                      + "\" title=\"Short, locally unique 'pet name' for the remote archive\" />" + pn.getName() + " ");
-            out.write("Location: <input type=\"text\" name=\"" + PARAM_LOC + "\" size=\"20\" value=\"" + pn.getLocation() 
-                      + "\" title=\"URL to the remote archive's archive/archive.txt\" /> ");
-            if (BlogManager.instance().authorizeRemote(user)) {
-                if (BlogManager.instance().syndicationScheduled(pn.getLocation()))
-                    out.write("Syndicate? <input type=\"checkbox\" name=\"" + PARAM_SYNDICATE 
-                              + "\" checked=\"true\" value=\"true\" title=\"If true, periodically pull down posts they have\" />");
-                else
-                    out.write("Syndicate? <input type=\"checkbox\" name=\"" + PARAM_SYNDICATE 
-                              + "\" value=\"true\" title=\"If true, periodically pull down posts they have\" />");
-
-                out.write("<a href=\"" + getSyndicateLink(user, pn.getLocation()) 
-                          + "\" title=\"Synchronize manually with the peer\">Sync manually</a> ");
-            } else {
-                out.write("You are not <a href=\"admin.jsp\">authorized</a> to syndicate with the archive ");
-            }
-            out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_DELETE_ARCHIVE + "\" /> ");
-            out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_UPDATE_ARCHIVE + "\" /> ");
-            out.write("</td></tr>\n");
-            out.write("</form>\n");
-        }
-
-        out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
-        writeAuthActionFields(out);
-        out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_ARCHIVE + "\" />");
-        out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_SYNDIE + "\" />");
-        out.write("<tr><td colspan=\"3\">");
-        out.write("<input type=\"checkbox\" name=\"" + PARAM_IS_PUBLIC + "\" value=\"true\" " + (newName.getIsPublic() ? " checked=\"true\" " : "") 
-                  + " title=\"If checked, this name can be shared with one click when posting\" />\n");
-        out.write("Name: <input type=\"text\" name=\"" + PARAM_NAME + "\" size=\"10\" value=\"" + newName.getName() 
-                  + "\" title=\"Short, locally unique 'pet name' for the remote archive\" /> ");
-        out.write("Location: <input type=\"text\" name=\"" + PARAM_LOC + "\" size=\"20\" value=\"" + newName.getLocation() 
-                  + "\" title=\"URL to the remote archive's archive/archive.txt\" /> ");
-        if (BlogManager.instance().authorizeRemote(user)) {
-            if (BlogManager.instance().syndicationScheduled(newName.getLocation()))
-                out.write("Syndicate? <input type=\"checkbox\" name=\"" + PARAM_SYNDICATE 
-                          + "\" checked=\"true\" value=\"true\" title=\"If true, periodically pull down posts they have\" />");
-            else
-                out.write("Syndicate? <input type=\"checkbox\" name=\"" + PARAM_SYNDICATE 
-                          + "\" value=\"true\" title=\"If true, periodically pull down posts they have\" />");
-        }
-
-        out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_ADD_ARCHIVE + "\" /> ");
-        out.write("</td></tr>\n");
-        out.write("</form>\n");
-        
-        out.write("<tr><td colspan=\"3\"><hr /></td></tr>\n");
-    }
-    
-    private void renderI2Phex(User user, PetNameDB db, String baseURI, PetName newName, PrintWriter out) throws IOException {
-        TreeSet names = new TreeSet();
-        for (Iterator iter = db.getNames().iterator(); iter.hasNext(); ) {
-            String name = (String)iter.next();
-            PetName pn = db.getByName(name);
-            if (PROTO_I2PHEX.equals(pn.getProtocol()))
-                names.add(name);
-        }
-        out.write("<tr><td colspan=\"3\"><b>I2Phex peers</b></td></tr>\n");
-        
-        for (Iterator iter = names.iterator(); iter.hasNext(); ) {
-            PetName pn = db.getByName((String)iter.next());
-            out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
-            writeAuthActionFields(out);
-            out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_I2PHEX + "\" />");
-            out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_I2P + "\" />");
-            out.write("<tr><td colspan=\"3\">");
-            out.write("<input type=\"checkbox\" name=\"" + PARAM_IS_PUBLIC + "\" value=\"true\" " + (pn.getIsPublic() ? " checked=\"true\" " : "") 
-                      + " title=\"If checked, this name can be shared with one click when posting\" />\n");
-            out.write("Name: <input type=\"hidden\" name=\"" + PARAM_NAME + "\" value=\"" + pn.getName() 
-                      + "\" title=\"Short, locally unique 'pet name' for the I2Phex peer\" />" + pn.getName() + " ");
-            out.write("Location: <input type=\"text\" name=\"" + PARAM_LOC + "\" size=\"3\" value=\"" + pn.getLocation() 
-                      + "\" title=\"I2P destination of the I2Phex peer\" /> ");
-            
-            out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_DELETE_PEER + "\" /> ");
-            out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_UPDATE_PEER + "\" /> ");
-            out.write("</td></tr>\n");
-            out.write("</form>\n");
-        }
-        
-        out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
-        writeAuthActionFields(out);
-        out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_I2PHEX + "\" />");
-        out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_I2P + "\" />");
-        out.write("<tr><td colspan=\"3\">");
-        out.write("<input type=\"checkbox\" name=\"" + PARAM_IS_PUBLIC + "\" value=\"true\" " + (newName.getIsPublic() ? " checked=\"true\" " : "") 
-                  + " title=\"If checked, this name can be shared with one click when posting\" />\n");
-        out.write("Name: <input type=\"text\" name=\"" + PARAM_NAME + "\" size=\"10\" value=\"" + newName.getName() 
-                  + "\" title=\"Short, locally unique 'pet name' for the I2Phex peer\" /> ");
-        out.write("Location: <input type=\"text\" name=\"" + PARAM_LOC + "\" size=\"3\" value=\"" + newName.getLocation() 
-                  + "\" title=\"I2P destination of the I2Phex peer\" /> ");
-
-        out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_ADD_PEER + "\" /> ");
-        out.write("</td></tr>\n");
-        out.write("</form>\n");
-        
-        out.write("<tr><td colspan=\"3\"><hr /></td></tr>\n");
-    }
-    private void renderEepsites(User user, PetNameDB db, String baseURI, PetName newName, PrintWriter out) throws IOException {
-        TreeSet names = new TreeSet();
-        for (Iterator iter = db.getNames().iterator(); iter.hasNext(); ) {
-            String name = (String)iter.next();
-            PetName pn = db.getByName(name);
-            if (PROTO_EEPSITE.equals(pn.getProtocol()))
-                names.add(name);
-        }
-        out.write("<tr><td colspan=\"3\"><b>Eepsites</b></td></tr>\n");
-        
-        for (Iterator iter = names.iterator(); iter.hasNext(); ) {
-            PetName pn = db.getByName((String)iter.next());
-            out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
-            writeAuthActionFields(out);
-            out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_EEPSITE + "\" />");
-            out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_I2P + "\" />");
-            out.write("<tr><td colspan=\"3\">");
-            out.write("<input type=\"checkbox\" name=\"" + PARAM_IS_PUBLIC + "\" value=\"true\" " + (pn.getIsPublic() ? " checked=\"true\" " : "") 
-                      + " title=\"If checked, this name can be shared with one click when posting\" />\n");
-            out.write("Name: <input type=\"hidden\" name=\"" + PARAM_NAME + "\" value=\"" + pn.getName() 
-                      + "\" title=\"Short, locally unique 'pet name' for the eepsite\" />" + pn.getName() + " ");
-            out.write("Location: <input type=\"text\" name=\"" + PARAM_LOC + "\" size=\"3\" value=\"" + pn.getLocation() 
-                      + "\" title=\"I2P destination of the eepsite\" /> ");
-            
-            out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_DELETE_EEPSITE + "\" /> ");
-            out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_UPDATE_EEPSITE + "\" /> ");
-            out.write("</td></tr>\n");
-            out.write("</form>\n");
-        }
-        
-        out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
-        writeAuthActionFields(out);
-        out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_EEPSITE + "\" />");
-        out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_I2P + "\" />");
-        out.write("<tr><td colspan=\"3\">");
-        out.write("<input type=\"checkbox\" name=\"" + PARAM_IS_PUBLIC + "\" value=\"true\" " + (newName.getIsPublic() ? " checked=\"true\" " : "") 
-                  + " title=\"If checked, this name can be shared with one click when posting\" />\n");
-        out.write("Name: <input type=\"text\" name=\"" + PARAM_NAME + "\" size=\"10\" value=\"" + newName.getName() 
-                  + "\" title=\"Short, locally unique 'pet name' for the eepsite\" /> ");
-        out.write("Location: <input type=\"text\" name=\"" + PARAM_LOC + "\" size=\"3\" value=\"" + newName.getLocation() 
-                  + "\" title=\"I2P destination of the eepsite\" /> ");
-
-        out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_ADD_EEPSITE + "\" /> ");
-        out.write("</td></tr>\n");
-        out.write("</form>\n");
-        
-        out.write("<tr><td colspan=\"3\"><hr /></td></tr>\n");
-    }
-    
-    private void renderTags(User user, PetNameDB db, String baseURI, PetName newName, PrintWriter out) throws IOException {
-        TreeSet names = new TreeSet();
-        for (Iterator iter = db.getNames().iterator(); iter.hasNext(); ) {
-            String name = (String)iter.next();
-            PetName pn = db.getByName(name);
-            if (PROTO_TAG.equals(pn.getProtocol()))
-                names.add(name);
-        }
-        out.write("<tr><td colspan=\"3\"><b>Favorite tags</b></td></tr>\n");
-        
-        for (Iterator iter = names.iterator(); iter.hasNext(); ) {
-            PetName pn = db.getByName((String)iter.next());
-            out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
-            writeAuthActionFields(out);
-            out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_TAG + "\" />");
-            out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_SYNDIE + "\" />");
-            out.write("<tr><td colspan=\"3\">");
-            out.write("<input type=\"checkbox\" name=\"" + PARAM_IS_PUBLIC + "\" value=\"true\" " + (pn.getIsPublic() ? " checked=\"true\" " : "") 
-                      + " title=\"If checked, this name can be shared with one click when posting\" />\n");
-            out.write("Name: <input type=\"hidden\" name=\"" + PARAM_NAME + "\" value=\"" + pn.getName() 
-                      + "\" />" + pn.getName() + " ");
-            out.write("<input type=\"hidden\" name=\"" + PARAM_LOC + "\" value=\"" + pn.getLocation() 
-                      + "\" /> ");
-            
-            out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_DELETE_TAG + "\" /> ");
-            out.write("</td></tr>\n");
-            out.write("</form>\n");
-        }
-        
-        out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
-        writeAuthActionFields(out);
-        out.write("<input type=\"hidden\" name=\"" + PARAM_PROTO + "\" value=\"" + PROTO_TAG + "\" />");
-        out.write("<input type=\"hidden\" name=\"" + PARAM_NET + "\" value=\"" + NET_SYNDIE + "\" />");
-        out.write("<tr><td colspan=\"3\">");
-        out.write("<input type=\"checkbox\" name=\"" + PARAM_IS_PUBLIC + "\" value=\"true\" " + (newName.getIsPublic() ? " checked=\"true\" " : "") 
-                  + " title=\"If checked, this name can be shared with one click when posting\" />\n");
-        out.write("Name: <input type=\"text\" name=\"" + PARAM_NAME + "\" size=\"10\" value=\"" + newName.getName() 
-                  + "\" title=\"Tag (or group of tags)\" /> ");
-
-        out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_ADD_TAG + "\" /> ");
-        out.write("</td></tr>\n");
-        out.write("</form>\n");
-        
-        out.write("<tr><td colspan=\"3\"><hr /></td></tr>\n");
-    }
-    
-    private void renderOther(User user, PetNameDB db, String baseURI, PetName newName, PrintWriter out) throws IOException {
-        TreeSet names = new TreeSet();
-        for (Iterator iter = db.getNames().iterator(); iter.hasNext(); ) {
-            String name = (String)iter.next();
-            PetName pn = db.getByName(name);
-            if (isRightProtocol(pn.getProtocol()))
-                names.add(name);
-        }
-        out.write("<tr><td colspan=\"3\"><b>Other addresses</b></td></tr>\n");
-        
-        for (Iterator iter = names.iterator(); iter.hasNext(); ) {
-            PetName pn = db.getByName((String)iter.next());
-            out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
-            writeAuthActionFields(out);
-            out.write("<tr><td colspan=\"3\">");
-            out.write("<input type=\"checkbox\" name=\"" + PARAM_IS_PUBLIC + "\" value=\"true\" " + (pn.getIsPublic() ? " checked=\"true\" " : "") 
-                      + " title=\"If checked, this name can be shared with one click when posting\" />\n");
-            out.write("Network: <input type=\"text\" name=\"" + PARAM_NET + "\" value=\"" + pn.getNetwork() 
-                      + "\" title=\"What network is this on - i2p, tor, internet, freenet, etc\" /> ");
-            out.write("Protocol: <input type=\"text\" name=\"" + PARAM_PROTO + "\" value=\"" + pn.getProtocol() 
-                      + "\" title=\"How do we access/interact with this resource\" /> ");
-            out.write("Name: <input type=\"hidden\" name=\"" + PARAM_NAME + "\" value=\"" + pn.getName() 
-                      + "\" title=\"Short, locally unique 'pet name' for the location\" />" + pn.getName() +" ");
-            out.write("Location: <input type=\"text\" name=\"" + PARAM_LOC + "\" size=\"3\" value=\"" + pn.getLocation() 
-                      + "\" title=\"URL\" /> ");
-            
-            out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_DELETE_OTHER + "\" /> ");
-            out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_UPDATE_OTHER + "\" /> ");
-            out.write("</td></tr>\n");
-            out.write("</form>\n");
-        }
-        
-        out.write("<form action=\"" + baseURI + "\" method=\"POST\">");
-        writeAuthActionFields(out);
-        
-        out.write("<tr><td colspan=\"3\">");
-        out.write("<input type=\"checkbox\" name=\"" + PARAM_IS_PUBLIC + "\" value=\"true\" " + (newName.getIsPublic() ? " checked=\"true\" " : "") 
-                  + " title=\"If checked, this name can be shared with one click when posting\" />\n");
-        out.write("Network: <input type=\"text\" name=\"" + PARAM_NET + "\" value=\"" + newName.getNetwork() 
-                  + "\" title=\"What network is this on - i2p, tor, internet, freenet, etc\" /> ");
-        out.write("Protocol: <input type=\"text\" name=\"" + PARAM_PROTO + "\" value=\"" + newName.getProtocol() 
-                  + "\" title=\"How do we access/interact with this resource\" /> ");
-        out.write("Name: <input type=\"text\" name=\"" + PARAM_NAME + "\" size=\"10\" value=\"" + newName.getName() 
-                  + "\" title=\"Short, locally unique 'pet name' for the location\" /> ");
-        out.write("Location: <input type=\"text\" name=\"" + PARAM_LOC + "\" size=\"3\" value=\"" + newName.getLocation() 
-                  + "\" title=\"URL\" /> ");
-
-        out.write("<input type=\"submit\" name=\"" + PARAM_ACTION + "\" value=\"" + ACTION_ADD_OTHER + "\" /> ");
-        out.write("</td></tr>\n");
-        out.write("</form>\n");        
-        
-        out.write("<tr><td colspan=\"3\"><hr /></td></tr>\n");
-    }
-    
-    /** build the 'other' name passed in */
-    private PetName buildNewName(HttpServletRequest req) { return buildNewName(req, null); }
-    /** build a petname based by the request passed in, if the new entry is of the given protocol */
-    private PetName buildNewName(HttpServletRequest req, String protocol) {
-        PetName pn = new PetName();
-        if (!isRightProtocol(req, protocol)) {
-            pn.setIsPublic(true);
-            pn.setName("");
-            pn.setLocation("");
-            if (protocol == null)
-                pn.setProtocol("");
-            else
-                pn.setProtocol(protocol);
-            pn.setNetwork("");
-            return pn;
-        } else {
-            pn = buildNewAddress(req);
-        }
-        return pn;
-    }
-    
-    private String getParam(HttpServletRequest req, String param) {
-        if (empty(req, param)) {
-            return "";
-        } else {
-            String val = req.getParameter(param);
-            return val;
-        }
-    }
-    
-    
-    private boolean isRightProtocol(HttpServletRequest req, String protocol) {
-        // if they hit submit, they are actually updating stuff, so don't include a 'new' one
-        if (!empty(req, PARAM_ACTION))
-            return false;
-    
-        return isRightProtocol(protocol, req.getParameter(PARAM_PROTO));
-    }
-    private boolean isRightProtocol(String proto) { return isRightProtocol((String)null, proto); }
-    private boolean isRightProtocol(String proto, String reqProto) {
-        if (empty(reqProto))
-            return false;
-        if (proto == null) {
-            if (PROTO_ARCHIVE.equals(reqProto) || 
-                PROTO_BLOG.equals(reqProto) ||
-                PROTO_EEPSITE.equals(reqProto) ||
-                PROTO_TAG.equals(reqProto) ||
-                PROTO_I2PHEX.equals(reqProto))
-                return false;
-            else // its something other than the four default types
-                return true;
-        } else {
-            return proto.equals(reqProto);
-        }
-    }
-    
-    protected String getTitle() { return "Syndie :: Addressbook"; }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/AdminServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/AdminServlet.java
deleted file mode 100644
index fa9b852ba385730485a6c0b6413669bbe5767bc1..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/AdminServlet.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-
-import javax.servlet.http.HttpServletRequest;
-
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.ThreadIndex;
-
-/**
- * Admin form
- *
- */
-public class AdminServlet extends BaseServlet {   
-    protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, 
-                                        int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
-        if (BlogManager.instance().authorizeRemote(user)) {
-            displayForm(user, req, out);
-        } else {
-            out.write("<tr><td colspan=\"3\"><span class=\"b_adminMsgErr\">You are not authorized to configure this Syndie instance</span></td></tr>\n");
-        }
-    }
-    
-    private void displayForm(User user, HttpServletRequest req, PrintWriter out) throws IOException {
-        out.write("<form action=\"" + req.getRequestURI() + "\" method=\"POST\">\n");
-        writeAuthActionFields(out);
-        out.write("<tr><td colspan=\"3\">");
-
-        // stop people from shooting themselves in the foot - only geeks can enable multiuser mode
-        // (by adding the single user flag to their syndie.config)
-        if (BlogManager.instance().isSingleUser())
-            out.write("<input type=\"hidden\" name=\"singleuser\" value=\"checked\" />\n");
-        /*
-        out.write("<em class=\"b_adminField\">Single user?</em> <input type=\"checkbox\" class=\"b_adminField\" name=\"singleuser\" ");
-        if (BlogManager.instance().isSingleUser())
-            out.write(" checked=\"true\" ");
-        out.write(" /><br />\n");
-
-        out.write("<span class=\"b_adminDescr\">If this is checked, the registration, admin, and remote passwords are unnecessary - anyone");
-        out.write("can register and administer Syndie, as well as use any remote functionality.  This should not be checked if untrusted");
-        out.write("parties can access this web interface.</span><br />\n");
-        */
-        out.write("<span class=\"b_adminField\">Default user:</span> <input class=\"b_adminField\" type=\"text\" name=\"defaultUser\" size=\"10\" value=\"");
-        out.write(BlogManager.instance().getDefaultLogin());
-        out.write("\" />\n");
-        out.write("<span class=\"b_adminField\">pass:</span> <input class=\"b_adminField\" type=\"text\" name=\"defaultPass\" size=\"10\" value=\"");
-        out.write(BlogManager.instance().getDefaultPass());
-        out.write("\"/><br />\n");
-        out.write("<span class=\"b_adminDescr\">If Syndie is in single user mode, it will create a new 'default' user automatically and use that ");
-        out.write("whenever you access Syndie unless you explicitly log in to another account.  If you want Syndie to use an existing account as ");
-        out.write("your default account, you can specify them here, in which case it will automatically log you in under that account.</span><br />\n");
-        out.write("<em class=\"b_adminField\">Registration password:</em> <input class=\"b_adminField\" type=\"text\" name=\"regpass\" size=\"10\" value=\"\" /><br />\n");
-        out.write("<span class=\"b_adminDescr\">Users must specify this password on the registration form to proceed.  If this is ");
-        out.write("blank, anyone can register.</span><br />\n");
-        out.write("<em class=\"b_adminField\">Remote password:</em> <input class=\"b_adminField\" type=\"text\" name=\"remotepass\" size=\"10\" value=\"\" /><br />\n");
-        out.write("<span class=\"b_adminDescr\">To access remote archives, users must first provide this password on their ");
-        out.write("metadata page.  Remote access is 'dangerous', as it allows the user to instruct ");
-        out.write("this Syndie instance to establish HTTP connections with arbitrary locations.  If ");
-        out.write("this field is not specified, no one can use remote archives.</span><br />\n");
-        out.write("<em class=\"b_adminField\">Default remote proxy host:</em> <input class=\"b_adminField\" type=\"text\" name=\"proxyhost\" size=\"20\" value=\"");
-        out.write(BlogManager.instance().getDefaultProxyHost());
-        out.write("\" /><br />\n");
-        out.write("<em class=\"b_adminField\">Default remote proxy port:</em> <input class=\"b_adminField\" type=\"text\" name=\"proxyport\" size=\"5\" value=\"");
-        out.write(BlogManager.instance().getDefaultProxyPort());
-        out.write("\" /><br />\n");
-        out.write("<span class=\"b_adminDescr\">This is the default HTTP proxy shown on the remote archive page.</span><br />\n");
-        out.write("<hr />\n");
-        out.write("<input class=\"b_adminSave\" type=\"submit\" name=\"action\" value=\"Save config\" />\n");
-
-        out.write("</td></tr>\n");
-        out.write("</form>\n");
-    }
-   
-    protected String getTitle() { return "Syndie :: Configuration"; }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ArchiveServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ArchiveServlet.java
deleted file mode 100644
index afd701b9cb0eb67ffa3d7bba8e3efedc9c21ab21..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/ArchiveServlet.java
+++ /dev/null
@@ -1,226 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import net.i2p.I2PAppContext;
-import net.i2p.data.Base64;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.data.ArchiveIndex;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogURI;
-
-/**
- *
- */
-public class ArchiveServlet extends HttpServlet {
-    
-    public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        handle(req, resp);
-    }
-    
-    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        handle(req, resp);
-    }
-    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        handle(req, resp);
-    }
-    public void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        handle(req, resp);
-    }
-    
-    public void handle(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        String path = req.getPathInfo();
-        if ( (path == null) || (path.trim().length() <= 1) ) {
-            renderRootIndex(resp);
-            return;
-        } else if (path.endsWith(Archive.INDEX_FILE)) {
-            renderSummary(req.getHeader("If-None-Match"), resp);
-        } else if (path.indexOf("export.zip") != -1) {
-            ExportServlet.export(req, resp);
-        } else {
-            String blog = getBlog(path);
-            if (path.endsWith(Archive.METADATA_FILE)) {
-                renderMetadata(blog, resp);
-            } else if (path.endsWith(".snd")) {
-                renderEntry(blog, getEntry(path), resp);
-            } else {
-                renderBlogIndex(blog, resp);
-            }
-        }
-    }
-    
-    private String getBlog(String path) {
-        //System.err.println("Blog: [" + path + "]");
-        int start = 0;
-        int end = -1;
-        int len = path.length();
-        for (int i = 0; i < len; i++) {
-            if (path.charAt(i) != '/') {
-                start = i;
-                break;
-            }
-        }
-        for (int j = start + 1; j < len; j++) {
-            if (path.charAt(j) == '/') {
-                end = j;
-                break;
-            }
-        }
-        if (end < 0) end = len;
-        String rv = path.substring(start, end);
-        //System.err.println("Blog: [" + path + "] rv: [" + rv + "]");
-        return rv;
-    }
-    
-    private long getEntry(String path) {
-        int start = path.lastIndexOf('/');
-        if (start < 0) return -1;
-        if (!(path.endsWith(".snd"))) return -1;
-        String rv = path.substring(start+1, path.length()-".snd".length());
-        //System.err.println("Entry: [" + path + "] rv: [" + rv + "]");
-        try {
-            return Long.parseLong(rv);
-        } catch (NumberFormatException nfe) {
-            return -1;
-        }
-    }
-    
-    private void renderRootIndex(HttpServletResponse resp) throws ServletException, IOException {
-        resp.setContentType("text/html;charset=utf-8");
-        //resp.setCharacterEncoding("UTF-8");
-        OutputStream out = resp.getOutputStream();
-        out.write(DataHelper.getUTF8("<a href=\"archive.txt\">archive.txt</a><br />\n"));
-        ArchiveIndex index = BlogManager.instance().getArchive().getIndex();
-        Set blogs = index.getUniqueBlogs();
-        for (Iterator iter = blogs.iterator(); iter.hasNext(); ) {
-            Hash blog = (Hash)iter.next();
-            String s = blog.toBase64();
-            out.write(DataHelper.getUTF8("<a href=\"" + s + "/\">" + s + "</a><br />\n"));
-        }
-        out.close();
-    }
-    
-    public static final String HEADER_EXPORT_CAPABLE = "X-Syndie-Export-Capable";
-    
-    private void renderSummary(String etag, HttpServletResponse resp) throws ServletException, IOException {
-        resp.setContentType("text/plain;charset=utf-8");
-        //resp.setCharacterEncoding("UTF-8");
-        ArchiveIndex index = BlogManager.instance().getArchive().getIndex();
-        byte[] indexUTF8 = DataHelper.getUTF8(index.toString());
-        String newEtag = "\"" + I2PAppContext.getGlobalContext().sha().calculateHash(indexUTF8).toBase64() + "\"";
-        if (etag != null && etag.equals(newEtag)) {
-            resp.sendError(304, "Archive not modified");
-            return;
-        }
-        resp.setHeader(HEADER_EXPORT_CAPABLE, "true");
-        resp.setHeader("ETag", newEtag);
-        OutputStream out = resp.getOutputStream();
-        out.write(indexUTF8);
-        out.close();
-    }
-    
-    private void renderMetadata(String blog, HttpServletResponse resp) throws ServletException, IOException {
-        byte b[] = Base64.decode(blog);
-        if ( (b == null) || (b.length != Hash.HASH_LENGTH) ) {
-            resp.sendError(404, "Invalid blog requested");
-            return;
-        }
-        Hash h = new Hash(b);
-        BlogInfo info = BlogManager.instance().getArchive().getBlogInfo(h);
-        if (info == null) {
-            resp.sendError(404, "Blog does not exist");
-            return;
-        }
-        resp.setContentType("application/x-syndie-meta");
-        OutputStream out = resp.getOutputStream();
-        info.write(out);
-        out.close();
-    }
-    
-    private void renderBlogIndex(String blog, HttpServletResponse resp) throws ServletException, IOException {
-        byte b[] = Base64.decode(blog);
-        if ( (b == null) || (b.length != Hash.HASH_LENGTH) ) {
-            resp.sendError(404, "Invalid blog requested");
-            return;
-        }
-        Hash h = new Hash(b);
-        
-        BlogInfo info = BlogManager.instance().getArchive().getBlogInfo(h);
-        if (info == null) {
-            resp.sendError(404, "Blog does not exist");
-            return;
-        }
-        resp.setContentType("text/html;charset=utf-8");
-        //resp.setCharacterEncoding("UTF-8");
-        OutputStream out = resp.getOutputStream();
-        out.write(DataHelper.getUTF8("<a href=\"..\">..</a><br />\n"));
-        out.write(DataHelper.getUTF8("<a href=\"" + Archive.METADATA_FILE + "\">" + Archive.METADATA_FILE + "</a><br />\n"));
-        List entries = new ArrayList(64);
-        BlogManager.instance().getArchive().getIndex().selectMatchesOrderByEntryId(entries, h, null);
-        for (int i = 0; i < entries.size(); i++) {
-            BlogURI entry = (BlogURI)entries.get(i);
-            out.write(DataHelper.getUTF8("<a href=\"" + entry.getEntryId() + ".snd\">" + entry.getEntryId() + ".snd</a><br />\n"));
-        }
-        out.close();
-    }
-        
-    private void renderEntry(String blog, long entryId, HttpServletResponse resp) throws ServletException, IOException {
-        byte b[] = Base64.decode(blog);
-        if ( (b == null) || (b.length != Hash.HASH_LENGTH) ) {
-            resp.sendError(404, "Invalid blog requested");
-            return;
-        }
-        Hash h = new Hash(b);
-        BlogInfo info = BlogManager.instance().getArchive().getBlogInfo(h);
-        if (info == null) {
-            resp.sendError(404, "Blog does not exist");
-            return;
-        }
-        File root = BlogManager.instance().getArchive().getArchiveDir();
-        File blogDir = new File(root, blog);
-        if (!blogDir.exists()) {
-            resp.sendError(404, "Blog does not exist");
-            return;
-        }
-        File entry = new File(blogDir, entryId + ".snd");
-        if (!entry.exists()) {
-            resp.sendError(404, "Entry does not exist");
-            return;
-        }
-        resp.setContentType("application/x-syndie-post");
-        dump(entry, resp);
-    }
-    
-    private void dump(File source, HttpServletResponse resp) throws ServletException, IOException {
-        FileInputStream in = null;
-        OutputStream out = null;
-        try {
-            in = new FileInputStream(source);
-            out = resp.getOutputStream();
-            byte buf[] = new byte[1024];
-            int read = 0;
-            while ( (read = in.read(buf)) != -1) 
-                out.write(buf, 0, read);
-            out.close();
-            in.close();
-        } finally {
-            if (in != null) try { in.close(); } catch (IOException ioe) {}
-            if (out != null) try { out.close(); } catch (IOException ioe) {}
-        }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ArchiveViewerBean.java b/apps/syndie/java/src/net/i2p/syndie/web/ArchiveViewerBean.java
deleted file mode 100644
index 56ed1b60b21fd2201ae8d1eb71820d49ba38a16e..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/ArchiveViewerBean.java
+++ /dev/null
@@ -1,822 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.TreeMap;
-
-import net.i2p.I2PAppContext;
-import net.i2p.client.naming.PetName;
-import net.i2p.client.naming.PetNameDB;
-import net.i2p.data.Base64;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Hash;
-import net.i2p.data.SigningPublicKey;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.ArchiveIndex;
-import net.i2p.syndie.data.Attachment;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.syndie.sml.HTMLRenderer;
-import net.i2p.syndie.sml.ThreadedHTMLRenderer;
-
-/**
- *
- */
-public class ArchiveViewerBean {
-    public static String getBlogName(String keyHash) {
-        BlogInfo info = BlogManager.instance().getArchive().getBlogInfo(new Hash(Base64.decode(keyHash)));
-        if (info == null)
-            return HTMLRenderer.sanitizeString(keyHash);
-        else
-            return HTMLRenderer.sanitizeString(info.getProperty("Name"));
-    }
-    
-    /** base64 encoded hash of the blog's public key, or null for no filtering by blog */
-    public static final String PARAM_BLOG = "blog";
-    /** base64 encoded tag to filter by, or blank for no filtering by tags */
-    public static final String PARAM_TAG = "tag";
-    /** entry id within the blog if we only want to see that one */
-    public static final String PARAM_ENTRY = "entry";
-    /** base64 encoded group within the user's filters */
-    public static final String PARAM_GROUP = "group";
-    /** how many entries per page to show at once */
-    public static final String PARAM_NUM_PER_PAGE = "pageSize";
-    /** which page of entries to render */
-    public static final String PARAM_PAGE_NUMBER = "pageNum";
-    /** should we expand each entry to show the full contents */
-    public static final String PARAM_EXPAND_ENTRIES = "expand";
-    /** should entries be rendered with the images shown inline */
-    public static final String PARAM_SHOW_IMAGES = "images";
-    /** should we regenerate an index to the archive before rendering */
-    public static final String PARAM_REGENERATE_INDEX = "regenerateIndex";
-    /** which attachment should we serve up raw */
-    public static final String PARAM_ATTACHMENT = "attachment";
-    /** we are replying to a particular blog/tag/entry/whatever (value == base64 encoded selector) */
-    public static final String PARAM_IN_REPLY_TO = "inReplyTo";
-    
-    /** prepopulate the subject field with the given value */
-    public static final String PARAM_SUBJECT = "replySubject";
-    /** prepopulate the tags with the given value */
-    public static final String PARAM_TAGS = "replyTags";
-    /** prepopulate the body with the given value */
-    public static final String PARAM_PARENT = "parentURI";
-    
-    /**
-     * Drop down multichooser:
-     *  blog://base64(key)
-     *  tag://base64(tag)
-     *  blogtag://base64(key)/base64(tag)
-     *  entry://base64(key)/entryId
-     *  group://base64(groupName)
-     *  ALL
-     */
-    public static final String PARAM_SELECTOR = "selector";
-    public static final String SEL_ALL = "ALL";
-    public static final String SEL_BLOG = "blog://";
-    public static final String SEL_TAG = "tag://";
-    public static final String SEL_BLOGTAG = "blogtag://";
-    public static final String SEL_ENTRY = "entry://";
-    public static final String SEL_GROUP = "group://";
-    /** submit field for the selector form */
-    public static final String PARAM_SELECTOR_ACTION = "action";
-    public static final String SEL_ACTION_SET_AS_DEFAULT = "Set as default";
-    
-    public static void renderBlogSelector(User user, Map parameters, Writer out) throws IOException {
-        String sel = getString(parameters, PARAM_SELECTOR);
-        String action = getString(parameters, PARAM_SELECTOR_ACTION);
-        if ( (sel != null) && (action != null) && (SEL_ACTION_SET_AS_DEFAULT.equals(action)) ) {
-            user.setDefaultSelector(HTMLRenderer.sanitizeString(sel, false));
-            BlogManager.instance().saveUser(user);
-        }
-        
-        out.write("<select class=\"b_selector\" name=\"");
-        out.write(PARAM_SELECTOR);
-        out.write("\">");
-        out.write("<option value=\"");
-        out.write(getDefaultSelector(user, parameters));
-        out.write("\">Default blog filter</option>\n");
-        out.write("<option value=\"");
-        out.write(SEL_ALL);
-        out.write("\">All posts from all blogs</option>\n");
-        
-        List groups = null;
-        if (user != null)
-            groups = user.getPetNameDB().getGroups();
-        if (groups != null) {
-            for (int i = 0; i < groups.size(); i++) {
-                String name = (String)groups.get(i);
-                out.write("<option value=\"group://" + Base64.encode(DataHelper.getUTF8(name)) + "\">" +
-                          "Group: " + HTMLRenderer.sanitizeString(name) + "</option>\n");
-            }
-        }
-        
-        Archive archive = BlogManager.instance().getArchive();
-        ArchiveIndex index = archive.getIndex();
-        
-        for (int i = 0; i < index.getNewestBlogCount(); i++) {
-            Hash cur = index.getNewestBlog(i);
-            PetName pn = user.getPetNameDB().getByLocation(cur.toBase64());
-            String knownName = null;
-            if (pn != null) {
-                knownName = pn.getName();
-            }
-            if ( (pn != null) && (pn.isMember("Ignore")) )
-                continue;
-            String blog = Base64.encode(cur.getData());
-            out.write("<option value=\"blog://" + blog + "\">");
-            out.write("New blog: ");
-            BlogInfo info = archive.getBlogInfo(cur);
-            String name = knownName;
-            if ( (name == null) && (info != null) ) 
-                name = info.getProperty(BlogInfo.NAME);
-            if (name != null)
-                name = HTMLRenderer.sanitizeString(name);
-            else
-                name = Base64.encode(cur.getData());
-            out.write(name);
-            out.write("</option>\n");
-        }
-        
-        ////List allTags = new ArrayList();
-        // perhaps sort this by name (even though it isnt unique...)
-        Set blogs = index.getUniqueBlogs();
-        for (Iterator iter = blogs.iterator(); iter.hasNext(); ) {
-            Hash cur = (Hash)iter.next();
-            PetName pn = user.getPetNameDB().getByLocation(cur.toBase64());
-            String knownName = null;
-            if (pn != null) {
-                knownName = pn.getName();
-            }
-            if ( (pn != null) && (pn.isMember("Ignore")) )
-                continue;
-         
-            String blog = Base64.encode(cur.getData());
-            out.write("<option value=\"blog://");
-            out.write(blog);
-            out.write("\">");
-            BlogInfo info = archive.getBlogInfo(cur);
-            String name = knownName;
-            if ( (name == null) && (info != null) ) 
-                name = info.getProperty(BlogInfo.NAME);
-            if (name != null)
-                name = HTMLRenderer.sanitizeString(name);
-            else
-                name = Base64.encode(cur.getData());
-            out.write(name);
-            if (info != null) {
-                int howMany = index.getBlogEntryCount(info.getKey().calculateHash());
-                if (howMany == 1)
-                    out.write(" [1 post]");
-                else
-                    out.write(" [" + howMany + " posts]");
-            }
-            out.write("</option>\n");
-            
-            /*
-            List tags = index.getBlogTags(cur);
-            for (int j = 0; j < tags.size(); j++) {
-                String tag = (String)tags.get(j);
-                if (false) {
-                    StringBuffer b = new StringBuffer(tag.length()*2);
-                    for (int k = 0; k < tag.length(); k++) {
-                        b.append((int)tag.charAt(k));
-                        b.append(' ');
-                    }
-                    System.out.println("tag in select: " + tag + ": " + b.toString());
-                }
-                
-                if (!allTags.contains(tag))
-                    allTags.add(tag);
-                out.write("<option value=\"blogtag://");
-                out.write(blog);
-                out.write("/");
-                byte utf8tag[] = DataHelper.getUTF8(tag);
-                String encoded = Base64.encode(utf8tag);
-                if (false) {
-                    byte utf8dec[] = Base64.decode(encoded);
-                    String travel = DataHelper.getUTF8(utf8dec);
-                    StringBuffer b = new StringBuffer();
-                    for (int k = 0; k < travel.length(); k++) {
-                        b.append((int)travel.charAt(k));
-                        b.append(' ');
-                    }
-                    b.append(" encoded into: ");
-                    for (int k = 0; k < encoded.length(); k++) {
-                        b.append((int)encoded.charAt(k));
-                        b.append(' ');
-                    }
-                    System.out.println("UTF8(unbase64(base64(UTF8(tag)))) == tag: " + b.toString());
-                }
-                out.write(encoded);
-                out.write("\">");
-                out.write(name);
-                out.write("- posts with the tag &quot;");
-                out.write(tag);
-                out.write("&quot;</option>\n");
-            }
-             */
-        }
-        /*
-        for (int i = 0; i < allTags.size(); i++) {
-            String tag = (String)allTags.get(i);
-            out.write("<option value=\"tag://");
-            out.write(Base64.encode(DataHelper.getUTF8(tag)));
-            out.write("\">Posts in any blog with the tag &quot;");
-            out.write(tag);
-            out.write("&quot;</option>\n");
-        }
-         */
-        out.write("</select>");
-        
-        int numPerPage = getInt(parameters, PARAM_NUM_PER_PAGE, 5);
-        int pageNum = getInt(parameters, PARAM_PAGE_NUMBER, 0);
-        boolean expandEntries = getBool(parameters, PARAM_EXPAND_ENTRIES, (user != null ? user.getShowExpanded() : false));
-        boolean showImages = getBool(parameters, PARAM_SHOW_IMAGES, (user != null ? user.getShowImages() : false));
-        
-        out.write("<input type=\"hidden\" name=\"" + PARAM_NUM_PER_PAGE+ "\" value=\"" + numPerPage+ "\" />");
-        out.write("<input type=\"hidden\" name=\"" + PARAM_PAGE_NUMBER+ "\" value=\"" + pageNum+ "\" />");
-        out.write("<input type=\"hidden\" name=\"" + PARAM_EXPAND_ENTRIES+ "\" value=\"" + expandEntries+ "\" />");
-        out.write("<input type=\"hidden\" name=\"" + PARAM_SHOW_IMAGES + "\" value=\"" + showImages + "\" />");
-        
-    }
-    
-    private static String getDefaultSelector(User user, Map parameters) {
-        if ( (user == null) || (user.getDefaultSelector() == null) )
-            return BlogManager.instance().getArchive().getDefaultSelector();
-        else
-            return user.getDefaultSelector();
-    }
-    
-    public static void renderBlogs(User user, Map parameters, Writer out, String afterPagination) throws IOException {
-        String blogStr = getString(parameters, PARAM_BLOG);
-        Hash blog = null;
-        if (blogStr != null) blog = new Hash(Base64.decode(blogStr));
-        if ( (blog != null) && (blog.getData() == null) ) blog = null;
-        String tag = getString(parameters, PARAM_TAG);
-        if (tag != null) tag = DataHelper.getUTF8(Base64.decode(tag));
-        
-        long entryId = -1;
-        if (blog != null) {
-            String entryIdStr = getString(parameters, PARAM_ENTRY);
-            try { 
-                entryId = Long.parseLong(entryIdStr); 
-            } catch (NumberFormatException nfe) {}
-        }
-        String group = getString(parameters, PARAM_GROUP);
-        if (group != null) group = DataHelper.getUTF8(Base64.decode(group));
-        
-        String sel = getString(parameters, PARAM_SELECTOR);
-        
-        if (getString(parameters, "action") != null) {
-            tag = null;
-            blog = null;
-            sel = null;
-            group = null;
-        }
-        
-        if ( (sel == null) && (blog == null) && (group == null) && (tag == null) )
-            sel = getDefaultSelector(user, parameters);
-        if (sel != null) {
-            Selector s = new Selector(sel);
-            blog = s.blog;
-            tag = s.tag;
-            entryId = s.entry;
-            group = s.group;
-        }
-        
-        int numPerPage = getInt(parameters, PARAM_NUM_PER_PAGE, 5);
-        int pageNum = getInt(parameters, PARAM_PAGE_NUMBER, 0);
-        boolean expandEntries = getBool(parameters, PARAM_EXPAND_ENTRIES, (user != null ? user.getShowExpanded() : false));
-        boolean showImages = getBool(parameters, PARAM_SHOW_IMAGES, (user != null ? user.getShowImages() : false));
-        boolean regenerateIndex = getBool(parameters, PARAM_REGENERATE_INDEX, false);
-        try {
-            renderBlogs(user, blog, tag, entryId, group, numPerPage, pageNum, expandEntries, showImages, regenerateIndex, sel, out, afterPagination);
-        } catch (IOException ioe) { 
-            ioe.printStackTrace();
-            throw ioe; 
-        } catch (RuntimeException re) {
-            re.printStackTrace();
-            throw re;
-        }
-    }
-    
-    public static class Selector {
-        public Hash blog;
-        public String tag;
-        public long entry;
-        public String group;
-        public Selector(String selector) {
-            entry = -1;
-            blog = null;
-            tag = null;
-            if (selector != null) {
-                if (selector.startsWith(SEL_BLOG)) {
-                    String blogStr = selector.substring(SEL_BLOG.length());
-                    //System.out.println("Selector [" + selector + "] blogString: [" + blogStr + "]");
-                    byte h[] = Base64.decode(blogStr);
-                    if (h != null)
-                        blog = new Hash(h);
-                    //else
-                    //    System.out.println("blog string does not decode properly: [" + blogStr + "]");
-                } else if (selector.startsWith(SEL_BLOGTAG)) {
-                    int tagStart = selector.lastIndexOf('/');
-                    String blogStr = selector.substring(SEL_BLOGTAG.length(), tagStart);
-                    blog = new Hash(Base64.decode(blogStr));
-                    if (blog.getData() == null) {
-                        System.out.println("Blog string [" + blogStr + "] does not decode");
-                        blog = null;
-                        return;
-                    }
-                    tag = selector.substring(tagStart+1);
-                    String origTag = tag;
-                    byte rawDecode[] = null;
-                    if (tag != null) {
-                        rawDecode = Base64.decode(tag);
-                        tag = DataHelper.getUTF8(rawDecode);
-                    }
-                    //System.out.println("Selector [" + selector + "] blogString: [" + blogStr + "] tag: [" + tag + "]");
-                    if (false && tag != null) {
-                        StringBuffer b = new StringBuffer(tag.length()*2);
-                        for (int j = 0; j < tag.length(); j++) {
-                            b.append((int)tag.charAt(j));
-                            if (rawDecode.length > j)
-                                b.append('.').append((int)rawDecode[j]);
-                            b.append(' ');
-                        }
-                        b.append("encoded as ");
-                        for (int j = 0; j < origTag.length(); j++) {
-                            b.append((int)origTag.charAt(j)).append(' ');
-                        }
-                        //System.out.println("selected tag: " + b.toString());
-                    }
-                } else if (selector.startsWith(SEL_TAG)) {
-                    tag = selector.substring(SEL_TAG.length());
-                    byte rawDecode[] = null;
-                    if (tag != null) {
-                        rawDecode = Base64.decode(tag);
-                        tag = DataHelper.getUTF8(rawDecode);
-                    }
-                    //System.out.println("Selector [" + selector + "] tag: [" + tag + "]");
-                    if (false && tag != null) {
-                        StringBuffer b = new StringBuffer(tag.length()*2);
-                        for (int j = 0; j < tag.length(); j++) {
-                            b.append((int)tag.charAt(j));
-                            if (rawDecode.length > j)
-                                b.append('.').append((int)rawDecode[j]);
-                            b.append(' ');
-                        }
-                        //System.out.println("selected tag: " + b.toString());
-                    }
-                } else if (selector.startsWith(SEL_ENTRY)) {
-                    int entryStart = selector.lastIndexOf('/');
-                    String blogStr = blogStr = selector.substring(SEL_ENTRY.length(), entryStart);
-                    String entryStr = selector.substring(entryStart+1);
-                    try {
-                        entry = Long.parseLong(entryStr);
-                        Hash h = new Hash(Base64.decode(blogStr));
-                        if (h.getData() != null)
-                            blog = h;
-                        //else
-                        //    System.out.println("Blog does not decode [" + blogStr + "]");
-                        //System.out.println("Selector [" + selector + "] blogString: [" + blogStr + "] entry: [" + entry + "]");
-                    } catch (NumberFormatException nfe) {}
-                } else if (selector.startsWith(SEL_GROUP)) {
-                    group = DataHelper.getUTF8(Base64.decode(selector.substring(SEL_GROUP.length())));
-                    //System.out.println("Selector [" + selector + "] group: [" + group + "]");
-                }
-            }
-        }
-    }
-    
-    private static void renderBlogs(User user, Hash blog, String tag, long entryId, String group, int numPerPage, int pageNum, 
-                                   boolean expandEntries, boolean showImages, boolean regenerateIndex, String selector, Writer out, String afterPagination) throws IOException {
-        Archive archive = BlogManager.instance().getArchive();
-        if (regenerateIndex)
-            archive.regenerateIndex();
-        ArchiveIndex index = archive.getIndex();
-        List entries = pickEntryURIs(user, index, blog, tag, entryId, group);
-        //System.out.println("Searching for " + blog + "/" + tag + "/" + entryId + "/" + pageNum + "/" + numPerPage + "/" + group);
-        //System.out.println("Entry URIs: " + entries);
-        
-        HTMLRenderer renderer = new HTMLRenderer(I2PAppContext.getGlobalContext());
-        int start = pageNum * numPerPage;
-        int end = start + numPerPage;
-        int pages = 1;
-        if (entries.size() <= 1) {
-            // just one, so no pagination, etc
-            start = 0;
-            end = 1;
-        } else {
-            if (end >= entries.size())
-                end = entries.size();
-            if ( (pageNum < 0) || (numPerPage <= 0) ) {
-                start = 0;
-                end = entries.size() - 1;
-            } else {
-                HTMLRenderer rend = new ThreadedHTMLRenderer(I2PAppContext.getGlobalContext());
-                pages = entries.size() / numPerPage;
-                if (numPerPage * pages < entries.size())
-                    pages++;
-                if (pageNum > 0) {
-                    String prevURL = null;
-                    prevURL = rend.getPageURL(blog, tag, entryId, group, numPerPage, pageNum-1, expandEntries, showImages);
-                    //System.out.println("prevURL: " + prevURL);
-                    out.write(" <a class=\"b_selectorPrevMore\" href=\"" + prevURL + "\">&lt;&lt;</a>");
-                } else {
-                    out.write(" <span class=\"b_selectorPrevNone\">&lt;&lt;</span> ");
-                }
-                out.write("<span class=\"b_selectorPage\">Page " + (pageNum+1) + " of " + pages + "</span>");
-                if (pageNum + 1 < pages) {
-                    String nextURL = null;
-                    nextURL = rend.getPageURL(blog, tag, entryId, group, numPerPage, pageNum+1, expandEntries, showImages);
-                    //System.out.println("nextURL: " + nextURL);
-                    out.write(" <a class=\"b_selectorNextMore\" href=\"" + nextURL + "\">&gt;&gt;</a>");
-                } else {
-                    out.write(" <span class=\"b_selectorNextNone\">&gt;&gt;</span>");
-                }
-            }
-        }
-        
-        /*
-        out.write(" <i>");
-        
-        if (showImages)
-            out.write("<a href=\"" + HTMLRenderer.getPageURL(blog, tag, entryId, group, numPerPage, pageNum, expandEntries, false) +
-                      "\">Hide images</a>");
-        else
-            out.write("<a href=\"" + HTMLRenderer.getPageURL(blog, tag, entryId, group, numPerPage, pageNum, expandEntries, true) +
-                      "\">Show images</a>");
-        
-        if (expandEntries)
-            out.write(" <a href=\"" + HTMLRenderer.getPageURL(blog, tag, entryId, group, numPerPage, pageNum, false, showImages) +
-                      "\">Hide details</a>");
-        else
-            out.write(" <a href=\"" + HTMLRenderer.getPageURL(blog, tag, entryId, group, numPerPage, pageNum, true, showImages) +
-                      "\">Expand details</a>");
-        
-        out.write("</i>");
-        */
-        
-        if (afterPagination != null) 
-            out.write(afterPagination);
-        
-        if (entries.size() <= 0) end = -1;
-        //System.out.println("Entries.size: " + entries.size() + " start=" + start + " end=" + end);
-        for (int i = start; i < end; i++) {
-            BlogURI uri = (BlogURI)entries.get(i);
-            EntryContainer c = archive.getEntry(uri);
-            try {
-                if (c == null)
-                    renderer.renderUnknownEntry(user, archive, uri, out);
-                else
-                    renderer.render(user, archive, c, out, !expandEntries, showImages);
-            } catch (RuntimeException e) {
-                e.printStackTrace();
-                throw e;
-            }
-        }
-    }
-    
-    public static List pickEntryURIs(User user, ArchiveIndex index, Hash blog, String tag, long entryId, String group) {
-        if ( (blog != null) && ( (blog.getData() == null) || (blog.getData().length != Hash.HASH_LENGTH) ) ) 
-            blog = null;
-        List rv = new ArrayList(16);
-        if ( (blog != null) && (entryId >= 0) ) {
-            rv.add(new BlogURI(blog, entryId));
-            return rv;
-        }
-        
-        if ( (group != null) && (user != null) ) {
-            List selectors = (List)user.getBlogGroups().get(group);
-            if (selectors != null) {
-                //System.out.println("Selectors for group " + group + ": " + selectors);
-                for (int i = 0; i < selectors.size(); i++) {
-                    String sel = (String)selectors.get(i);
-                    Selector s = new Selector(sel);
-                    if ( (s.entry >= 0) && (s.blog != null) && (s.group == null) && (s.tag == null) )
-                        rv.add(new BlogURI(s.blog, s.entry));
-                    else
-                        index.selectMatchesOrderByEntryId(rv, s.blog, s.tag);
-                }
-            }
-            PetNameDB db = user.getPetNameDB();
-            for (Iterator iter = db.getNames().iterator(); iter.hasNext(); ) {
-                String name = (String)iter.next();
-                PetName pn = db.getByName(name);
-                if ("syndie".equals(pn.getNetwork()) && "syndieblog".equals(pn.getProtocol()) && pn.isMember(group)) {
-                    byte pnLoc[] = Base64.decode(pn.getLocation());
-                    if (pnLoc != null) {
-                        Hash pnHash = new Hash(pnLoc);
-                        index.selectMatchesOrderByEntryId(rv, pnHash, null);
-                    }
-                }
-            }
-            sort(rv);
-            if (rv.size() > 0)
-                return rv;
-        }
-        index.selectMatchesOrderByEntryId(rv, blog, tag);
-        filterIgnored(user, rv);
-        return rv;
-    }
-    
-    private static void filterIgnored(User user, List uris) {
-        for (int i = 0; i < uris.size(); i++) {
-            BlogURI uri = (BlogURI)uris.get(i);
-            Hash k = uri.getKeyHash();
-            if (k == null) continue;
-            PetName pn = user.getPetNameDB().getByLocation(k.toBase64());
-            if ( (pn != null) && (pn.isMember("Ignore")) ) {
-                uris.remove(i);
-                i--;
-            }
-        }
-    }
-    
-    private static void sort(List uris) {
-        TreeMap ordered = new TreeMap();
-        while (uris.size() > 0) {
-            BlogURI uri = (BlogURI)uris.remove(0);
-            int off = 0;
-            while (ordered.containsKey(new Long(0 - off - uri.getEntryId())))
-                off++;
-            ordered.put(new Long(0-off-uri.getEntryId()), uri);
-        }
-        for (Iterator iter = ordered.values().iterator(); iter.hasNext(); )
-            uris.add(iter.next());
-    }
-    
-    public static final String getString(Map parameters, String param) {
-        if ( (parameters == null) || (parameters.get(param) == null) )
-            return null;
-        Object vals = parameters.get(param);
-        if (vals.getClass().isArray()) {
-            String v[] = (String[])vals;
-            if (v.length > 0)
-                return ((String[])vals)[0];
-            else
-                return null;
-        } else if (vals instanceof Collection) {
-            Collection c = (Collection)vals;
-            if (c.size() > 0)
-                return (String)c.iterator().next();
-            else
-                return null;
-        } else if (vals instanceof String) {
-            return (String)vals;
-        } else {
-            return null;
-        }
-    }
-    public static final String[] getStrings(Map parameters, String param) {
-        if ( (parameters == null) || (parameters.get(param) == null) )
-            return null;
-        Object vals = parameters.get(param);
-        if (vals.getClass().isArray()) {
-            return (String[])vals;
-        } else if (vals instanceof Collection) {
-            Collection c = (Collection)vals;
-            if (c.size() <= 0) return null;
-            String rv[] = new String[c.size()];
-            int i = 0;
-            for (Iterator iter = c.iterator(); iter.hasNext(); i++) 
-                rv[i] = (String)iter.next();
-            return rv;
-        } else {
-            return null;
-        }
-    }
-    
-    private static final int getInt(Map param, String key, int defaultVal) {
-        String val = getString(param, key);
-        if (val != null) {
-            try { return Integer.parseInt(val); } catch (NumberFormatException nfe) {}
-        }
-        return defaultVal;
-    }
-    
-    private static final boolean getBool(Map param, String key, boolean defaultVal) {
-        String val = getString(param, key);
-        if (val != null) {
-            return ("true".equals(val) || "yes".equals(val));
-        }
-        return defaultVal;
-    }
-    
-    public static void renderAttachment(Map parameters, OutputStream out) throws IOException {
-        renderAttachment(getAttachment(parameters), out);
-    }
-    public static void renderAttachment(Attachment a, OutputStream out) throws IOException {
-        if (a == null) {
-            renderInvalidAttachment(out);
-        } else {
-            InputStream data = a.getDataStream();
-            byte buf[] = new byte[1024];
-            int read = 0;
-            while ( (read = data.read(buf)) != -1) 
-                out.write(buf, 0, read);
-            data.close();
-        }
-    }
-    
-    public static final String getAttachmentContentType(Map parameters) {
-        return getAttachmentContentType(getAttachment(parameters));
-    }
-    public static final String getAttachmentContentType(Attachment attachment) {
-        if (attachment == null) 
-            return "text/html";
-        String mime = attachment.getMimeType();
-        if ( (mime != null) && ((mime.startsWith("image/") || mime.startsWith("text/plain"))) )
-            return mime;
-        return "application/octet-stream";
-    }
-    
-    public static final boolean getAttachmentShouldShowInline(Map parameters) {
-        return getAttachmentShouldShowInline(getAttachment(parameters));
-    }
-    public static final boolean getAttachmentShouldShowInline(Attachment a) {
-        if (a == null) 
-            return true;
-        String mime = a.getMimeType();
-        if ( (mime != null) && ((mime.startsWith("image/") || mime.startsWith("text/plain"))) )
-            return true;
-        else
-            return false;
-    }
-    
-    public static final int getAttachmentContentLength(Map parameters) {
-        return getAttachmentContentLength(getAttachment(parameters));
-    }
-    public static final int getAttachmentContentLength(Attachment a) {
-        if (a != null)
-            return a.getDataLength();
-        else
-            return -1;
-    }
-    
-    public static final String getAttachmentFilename(Map parameters) {
-        return getAttachmentFilename(getAttachment(parameters));
-    }
-    public static final String getAttachmentFilename(Attachment a) {
-        if (a != null)
-            return a.getName();
-        else
-            return "attachment.dat";
-    }
-    
-    private static final Attachment getAttachment(Map parameters) {
-        String blogStr = getString(parameters, PARAM_BLOG);
-        Hash blog = null;
-        if (blogStr != null) blog = new Hash(Base64.decode(blogStr));
-        long entryId = -1;
-        if (blogStr != null) {
-            String entryIdStr = getString(parameters, PARAM_ENTRY);
-            try { 
-                entryId = Long.parseLong(entryIdStr); 
-            } catch (NumberFormatException nfe) {}
-        }
-        int attachment = getInt(parameters, PARAM_ATTACHMENT, -1);
-        
-        Archive archive = BlogManager.instance().getArchive();
-        EntryContainer entry = archive.getEntry(new BlogURI(blog, entryId));
-        if ( (entry != null) && (attachment >= 0) && (attachment < entry.getAttachments().length) ) {
-            return entry.getAttachments()[attachment];
-        }
-        return null;
-    }
-    
-    private static void renderInvalidAttachment(OutputStream out) throws IOException {
-        out.write(DataHelper.getUTF8("<span class=\"b_msgErr\">No such entry, or no such attachment</span>"));
-    }
-    
-    private static String getURL(String uri, Map parameters) {
-        StringBuffer rv = new StringBuffer(128);
-        rv.append(uri);
-        rv.append('?');
-        if (parameters != null) {
-            for (Iterator iter = parameters.keySet().iterator(); iter.hasNext(); ) {
-                String key = (String)iter.next();
-                String vals[] = getStrings(parameters, key);
-                // we are already looking at the page with the given parameters, no need to further sanitize
-                if ( (key != null) && (vals != null) ) 
-                    for (int i = 0; i < vals.length; i++)
-                        rv.append(key).append('=').append(vals[i]).append('&');
-            }
-        }
-        return rv.toString();
-    }
-    
-    private static void updateMetadata(User viewer, Map parameters, Writer out) throws IOException {
-        if ( (viewer == null) || (!viewer.getAuthenticated()) )
-            return;
-        String blogStr = getString(parameters, PARAM_BLOG);
-        if (blogStr != null) { 
-            Hash blog = new Hash(Base64.decode(blogStr));
-            Archive archive = BlogManager.instance().getArchive();
-            BlogInfo info = archive.getBlogInfo(blog);
-            if (info != null) {
-                boolean isUser = viewer.getBlog().equals(info.getKey().calculateHash());
-                if (!isUser)
-                    return;
-                Properties toSave = new Properties();
-                String existing[] = info.getProperties();
-                for (int i = 0; i < existing.length; i++) {
-                    String newVal = getString(parameters, existing[i]);
-                    if ( (newVal != null) && (newVal.length() > 0) )
-                        toSave.setProperty(existing[i], newVal.trim());
-                    else
-                        toSave.setProperty(existing[i], info.getProperty(existing[i]));
-                }
-                boolean saved = BlogManager.instance().updateMetadata(viewer, blog, toSave);
-                if (saved)
-                    out.write("<p><em class=\"b_msgOk\">Blog metadata saved</em></p>\n");
-                else
-                    out.write("<p><em class=\"b_msgErr\">Blog metadata could not be saved</em></p>\n");
-            }
-        }
-    }
-    
-    /**
-     * @param currentURI URI of the with current page without any parameters tacked on
-     */
-    public static void renderMetadata(User viewer, String currentURI, Map parameters, Writer out) throws IOException {
-        if (parameters.get("action") != null) {
-            updateMetadata(viewer, parameters, out);
-        }
-        String blogStr = getString(parameters, PARAM_BLOG);
-        if (blogStr != null) { 
-            Hash blog = new Hash(Base64.decode(blogStr));
-            Archive archive = BlogManager.instance().getArchive();
-            BlogInfo info = archive.getBlogInfo(blog);
-            if (info == null) {
-                out.write("Blog " + blog.toBase64() + " does not exist");
-                return;
-            }
-            boolean isUser = ( (viewer != null) && (viewer.getAuthenticated()) && (viewer.getBlog().equals(info.getKey().calculateHash())) );
-            String props[] = info.getProperties();
-            if (isUser) {
-                out.write("<form action=\"" + getURL(currentURI, parameters) + "\" method=\"GET\">\n");
-                out.write("<input type=\"hidden\" name=\"submit_blog\" value=\"" + blog.toBase64() + "\" />\n");
-            }
-            out.write("<table class=\"b_meta\" border=\"0\">");
-            for (int i = 0; i < props.length; i++) {
-                if (props[i].equals(BlogInfo.OWNER_KEY)) {
-                    out.write("<tr class=\"b_metaBlog\"><td class=\"b_metaBlog\"><span class=\"b_metaBlog\">Blog:</span></td>");
-                    out.write("<td class=\"b_metaBlog\">" + Base64.encode(blog.getData()) + "</td></tr>\n");
-                } else if (props[i].equals(BlogInfo.SIGNATURE)) {
-                    continue;
-                } else if (props[i].equals(BlogInfo.POSTERS)) {
-                    SigningPublicKey keys[] = info.getPosters();
-                    if ( (keys != null) && (keys.length > 0) ) {
-                        out.write("<tr class=\"b_metaAuthor\"><td class=\"b_metaAuthor\"><span class=\"b_metaAuthor\">Allowed authors:</span></td>");
-                        out.write("<td class=\"b_metaAuthor\">");
-                        for (int j = 0; j < keys.length; j++) {
-                            out.write("<span class=\"b_metaAuthor\">" + keys[j].calculateHash().toBase64() + "</span>");
-                            if (j + 1 < keys.length)
-                                out.write("<br />\n");
-                        }
-                        out.write("</td></tr>\n");
-                    }
-                } else {
-                    String field = HTMLRenderer.sanitizeString(props[i]);
-                    String val = HTMLRenderer.sanitizeString(info.getProperty(props[i]));
-                    out.write("<tr class=\"b_metaField\"><td class=\"b_metaField\"><span class=\"b_metaField\">" + field
-                              + ":</span></td><td class=\"b_metaValue\"><span class=\"b_metaValue\">" + val + "</span></td></tr>\n");
-                    
-                    if (isUser && (!field.equals("Edition")))
-                        out.write("<tr class=\"b_metaField\"><td>&nbsp;</td><td class=\"b_metaValue\"><input type=\"text\" name=\"" 
-                                  + HTMLRenderer.sanitizeTagParam(props[i]) + "\" value=\"" 
-                                  + HTMLRenderer.sanitizeTagParam(info.getProperty(props[i])) + "\" size=\"40\" ></td></tr>");
-                }
-            }
-            List tags = BlogManager.instance().getArchive().getIndex().getBlogTags(blog);
-            if ( (tags != null) && (tags.size() > 0) ) {
-                out.write("<tr class=\"b_metaTags\"><td class=\"b_metaTags\"><span class=\"b_metaTags\">Known tags:</span></td><td class=\"b_metaTags\">");
-                for (int i = 0; i < tags.size(); i++) {
-                    String tag = (String)tags.get(i);
-                    HTMLRenderer rend = new HTMLRenderer(I2PAppContext.getGlobalContext());
-                    out.write("<a class=\"b_metaTag\" href=\"" + rend.getPageURL(blog, tag, -1, -1, -1, false, false) + "\">" +
-                              HTMLRenderer.sanitizeString(tag) + "</a> ");
-                }
-                out.write("</td></tr>");
-            }
-            if (isUser)
-                out.write("<tr class=\"b_metaField\"><td colspan=\"2\" class=\"b_metaField\"><input type=\"submit\" name=\"action\" value=\"Save changes\" class=\"b_metaSave\" /></td></tr>\n");
-            out.write("</table>");
-        } else {
-            out.write("<span class=\"b_metaMsgErr\">Blog not specified</span>");
-        }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/BaseServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/BaseServlet.java
deleted file mode 100644
index aba868a15d0e5edcd73021bd50bb70a4148a3734..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/BaseServlet.java
+++ /dev/null
@@ -1,1302 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.io.Reader;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Properties;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.TreeSet;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import net.i2p.I2PAppContext;
-import net.i2p.client.naming.PetName;
-import net.i2p.client.naming.PetNameDB;
-import net.i2p.data.Base64;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.FilteredThreadIndex;
-import net.i2p.syndie.data.ThreadIndex;
-import net.i2p.syndie.data.ThreadNode;
-import net.i2p.syndie.sml.HTMLRenderer;
-import net.i2p.syndie.sml.ThreadedHTMLRenderer;
-import net.i2p.util.FileUtil;
-import net.i2p.util.Log;
-
-/**
- * Base servlet for handling request and rendering the templates
- *
- */
-public abstract class BaseServlet extends HttpServlet {
-    protected static final String PARAM_AUTH_ACTION = "syndie.auth";
-    protected static long _authNonce;
-    protected I2PAppContext _context;
-    protected Log _log;
-    
-    public void init() throws ServletException { 
-        super.init();
-        _context = I2PAppContext.getGlobalContext();
-        _log = _context.logManager().getLog(getClass());
-        _authNonce = _context.random().nextLong();
-    }
-    
-    protected boolean authAction(HttpServletRequest req) {
-        return authAction(req.getParameter(PARAM_AUTH_ACTION));
-    }
-    protected boolean authAction(String auth) {
-        if (auth == null) {
-            return false;
-        } else {
-            try { 
-                boolean rv = (Long.valueOf(auth).longValue() == _authNonce); 
-                return rv;
-            } catch (NumberFormatException nfe) {
-                return false;
-            }
-        }
-    }
-    
-    /**
-     * write out hidden fields for params that need to be tacked onto an http request that updates 
-     * data, to prevent spoofing
-     */
-    protected void writeAuthActionFields(Writer out) throws IOException {
-        out.write("<input type=\"hidden\" name=\"" + PARAM_AUTH_ACTION + "\" value=\"" + _authNonce + "\" />");
-    }
-    protected String getAuthActionFields() throws IOException {
-        return "<input type=\"hidden\" name=\"" + PARAM_AUTH_ACTION + "\" value=\"" + _authNonce + "\" />";
-    }
-    /** 
-     * key=value& of params that need to be tacked onto an http request that updates data, to 
-     * prevent spoofing 
-     */
-    protected static String getAuthActionParams() { return PARAM_AUTH_ACTION + '=' + _authNonce + "&amp;"; }
-    /** 
-     * key=value& of params that need to be tacked onto an http request that updates data, to 
-     * prevent spoofing 
-     */
-    public static void addAuthActionParams(StringBuffer buf) { 
-        buf.append(PARAM_AUTH_ACTION).append('=').append(_authNonce).append("&amp;"); 
-    }
-    
-    public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        req.setCharacterEncoding("UTF-8");
-        resp.setCharacterEncoding("UTF-8");
-        resp.setContentType("text/html;charset=UTF-8");
-        resp.setHeader("cache-control", "no-cache");
-        resp.setHeader("pragma", "no-cache");
-        
-        User user = (User)req.getSession().getAttribute("user");
-        String login = req.getParameter("login");
-        String pass = req.getParameter("password");
-        String action = req.getParameter("action");
-        boolean forceNewIndex = false;
-
-        boolean authAction = authAction(req);
-        
-        if (req.getParameter("regenerateIndex") != null)
-            forceNewIndex = true;
-
-        User oldUser = user;
-        if (authAction)
-            user = handleRegister(user, req);
-        if (oldUser != user)
-            forceNewIndex = true;
-        
-        if (user == null) {
-            if ("Login".equals(action)) {
-                user = BlogManager.instance().login(login, pass); // ignore failures - user will just be unauthorized
-                if (!user.getAuthenticated()) {
-                    user = BlogManager.instance().getDefaultUser();
-                    if (_log.shouldLog(Log.INFO))
-                        _log.info("Explicit login failed for [" + login + "], using default login");
-                } else {
-                    if (_log.shouldLog(Log.INFO))
-                        _log.info("Explicit login successful for [" + login + "]");
-                }
-            } else {
-                user = BlogManager.instance().getDefaultUser();
-                if (_log.shouldLog(Log.INFO))
-                    _log.info("Implicit login for the default user");
-            }
-            forceNewIndex = true;
-        } else if (authAction && "Login".equals(action)) {
-            user = BlogManager.instance().login(login, pass); // ignore failures - user will just be unauthorized
-            if (!user.getAuthenticated()) {
-                if (_log.shouldLog(Log.INFO))
-                    _log.info("Explicit relogin failed for [" + login + "] from [" + user.getUsername() + "], using default user");
-                user = BlogManager.instance().getDefaultUser();
-            } else {
-                if (_log.shouldLog(Log.INFO))
-                    _log.info("Explicit relogin successful for [" + login + "] from [" + user.getUsername() + "]");
-            }
-            forceNewIndex = true;
-        } else if (authAction && "Logout".equals(action)) {
-            if (_log.shouldLog(Log.INFO))
-                _log.info("Explicit logout successful for [" + user.getUsername() + "], using default login");
-            user = BlogManager.instance().getDefaultUser();
-            forceNewIndex = true;
-        }
-        
-        req.getSession().setAttribute("user", user);
-        
-        if (authAction) {
-            handleAdmin(user, req);
-        
-            forceNewIndex = handleAddressbook(user, req) || forceNewIndex;
-            forceNewIndex = handleBookmarking(user, req) || forceNewIndex;
-            forceNewIndex = handleManageTags(user, req) || forceNewIndex;
-            handleUpdateProfile(user, req);
-            req.setAttribute(BaseServlet.class.getName() + ".auth", "true");
-        }
-        
-        // the 'dataImported' flag is set by successful fetches in the SyndicateServlet/RemoteArchiveBean
-        if (user.resetDataImported()) {
-            forceNewIndex = true;
-            if (_log.shouldLog(Log.INFO))
-                _log.info("Data imported, force regenerate");
-        }
-        
-        FilteredThreadIndex index = (FilteredThreadIndex)req.getSession().getAttribute("threadIndex");
-        
-        boolean authorOnly = Boolean.valueOf(req.getParameter(ThreadedHTMLRenderer.PARAM_THREAD_AUTHOR)).booleanValue();
-        if ( (index != null) && (authorOnly != index.getFilterAuthorsByRoot()) )
-            forceNewIndex = true;
-        
-        Collection tags = getFilteredTags(req);
-        Collection filteredAuthors = getFilteredAuthors(req);
-        boolean tagsChanged = ( (index != null) && (!index.getFilteredTags().equals(tags)) );
-        boolean authorsChanged = ( (index != null) && (!index.getFilteredAuthors().equals(filteredAuthors)) );
-        
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("authorOnly=" + authorOnly + " forceNewIndex? " + forceNewIndex + " authors=" + filteredAuthors);
-        
-        if (forceNewIndex || (index == null) || (tagsChanged) || (authorsChanged) ) {
-            index = new FilteredThreadIndex(user, BlogManager.instance().getArchive(), getFilteredTags(req), filteredAuthors, authorOnly);
-            req.getSession().setAttribute("threadIndex", index);
-            if (_log.shouldLog(Log.INFO))
-                _log.info("New filtered index created (forced? " + forceNewIndex + ", tagsChanged? " + tagsChanged + ", authorsChanged? " + authorsChanged + ")");
-        }
-        
-        render(user, req, resp, index);
-    }
-    protected void render(User user, HttpServletRequest req, HttpServletResponse resp, ThreadIndex index) throws IOException, ServletException {
-        render(user, req, resp.getWriter(), index);
-    }
-    protected boolean isAuthed(HttpServletRequest req) {
-        String auth = (String)req.getAttribute(BaseServlet.class.getName() + ".auth");
-        return (auth != null) && (Boolean.valueOf(auth).booleanValue());
-    }
-    
-    private boolean handleBookmarking(User user, HttpServletRequest req) {
-        if (!user.getAuthenticated())
-            return false;
-        
-        boolean rv = false;
-        
-        String loc = req.getParameter(ThreadedHTMLRenderer.PARAM_ADD_TO_GROUP_LOCATION);
-        String group = req.getParameter(ThreadedHTMLRenderer.PARAM_ADD_TO_GROUP_NAME);
-        if ( (loc != null) && (group != null) && (group.trim().length() > 0) ) {
-            try {
-                Hash key = new Hash();
-                key.fromBase64(loc);
-                PetNameDB db = user.getPetNameDB();
-                PetName pn = db.getByLocation(loc);
-                boolean isNew = false;
-                if (pn == null) {
-                    isNew = true;
-                    BlogInfo info = BlogManager.instance().getArchive().getBlogInfo(key);
-                    String name = null;
-                    if (info != null)
-                        name = info.getProperty(BlogInfo.NAME);
-                    else
-                        name = loc.substring(0,6);
-
-                    if (db.containsName(name)) {
-                        int i = 0;
-                        while (db.containsName(name + i))
-                            i++;
-                        name = name + i;
-                    }
-
-                    pn = new PetName(name, AddressesServlet.NET_SYNDIE, AddressesServlet.PROTO_BLOG, loc);
-                }
-                pn.addGroup(group);
-                if (isNew)
-                    db.add(pn);
-                BlogManager.instance().saveUser(user);
-                // if we are ignoring someone, we need to recalculate the filters
-                if (FilteredThreadIndex.GROUP_IGNORE.equals(group))
-                    rv = true;
-            } catch (DataFormatException dfe) {
-                // bad loc, ignore
-            }
-        }
-        
-        String name = req.getParameter(ThreadedHTMLRenderer.PARAM_REMOVE_FROM_GROUP_NAME);
-        group = req.getParameter(ThreadedHTMLRenderer.PARAM_REMOVE_FROM_GROUP);
-        if ( (name != null) && (name.trim().length() > 0) ) {
-            PetNameDB db = user.getPetNameDB();
-            PetName pn = db.getByName(name);
-            boolean changed = false;
-            if (pn != null) {
-                if ( (group != null) && (group.trim().length() > 0) ) {
-                    // just remove them from the group
-                    changed = pn.isMember(group);
-                    pn.removeGroup(group);
-                    if ( (changed) && (FilteredThreadIndex.GROUP_IGNORE.equals(group)) )
-                        rv = true;
-                } else {
-                    // remove it completely
-                    if (pn.isMember(FilteredThreadIndex.GROUP_IGNORE))
-                        rv = true;
-                    db.remove(pn);
-                    changed = true;
-                }
-            }
-            if (changed)
-                BlogManager.instance().saveUser(user);
-        }
-        
-        if (rv)
-            _log.debug("Bookmarking required rebuild");
-        return rv;
-    }
-    
-    private boolean handleManageTags(User user, HttpServletRequest req) {
-        if (!user.getAuthenticated())
-            return false;
-        
-        boolean rv = false;
-        
-        String tag = req.getParameter(ThreadedHTMLRenderer.PARAM_ADD_TAG);
-        if ( (tag != null) && (tag.trim().length() > 0) ) {
-            tag = HTMLRenderer.sanitizeString(tag, false);
-            String name = tag;
-            PetNameDB db = user.getPetNameDB();
-            PetName pn = db.getByLocation(tag);
-            if (pn == null) {
-                if (db.containsName(name)) {
-                    int i = 0;
-                    while (db.containsName(name + i))
-                        i++;
-                    name = tag + i;
-                }
-
-                pn = new PetName(name, AddressesServlet.NET_SYNDIE, AddressesServlet.PROTO_TAG, tag);
-                db.add(pn);
-                BlogManager.instance().saveUser(user);
-            }
-        }
-        
-        return false;
-    }
-    
-    private boolean handleAddressbook(User user, HttpServletRequest req) {
-        if ( (!user.getAuthenticated()) || (empty(AddressesServlet.PARAM_ACTION)) ) {
-            return false;
-        }
-        
-        String action = req.getParameter(AddressesServlet.PARAM_ACTION);
-        
-        if (AddressesServlet.ACTION_ADD_TAG.equals(action)) {
-            String name = req.getParameter(AddressesServlet.PARAM_NAME);
-            if ((name != null) && (name.trim().length() > 0) && (!user.getPetNameDB().containsName(name)) ) {
-                PetName pn = new PetName(name, AddressesServlet.NET_SYNDIE, AddressesServlet.PROTO_TAG, name);
-                user.getPetNameDB().add(pn);
-                BlogManager.instance().saveUser(user);
-            }
-            return false;
-        } else if ( (AddressesServlet.ACTION_ADD_ARCHIVE.equals(action)) || 
-             (AddressesServlet.ACTION_ADD_BLOG.equals(action)) ||
-             (AddressesServlet.ACTION_ADD_EEPSITE.equals(action)) || 
-             (AddressesServlet.ACTION_ADD_OTHER.equals(action)) ||
-             (AddressesServlet.ACTION_ADD_PEER.equals(action)) ) {
-            PetName pn = buildNewAddress(req);
-            if ( (pn != null) && (pn.getName() != null) && (pn.getName().trim().length() > 0) && (pn.getLocation() != null) && 
-                 (!user.getPetNameDB().containsName(pn.getName())) ) {
-                user.getPetNameDB().add(pn);
-                BlogManager.instance().saveUser(user);
-                
-                updateSyndication(user, pn.getLocation(), !empty(req, AddressesServlet.PARAM_SYNDICATE));
-                
-                if (pn.isMember(FilteredThreadIndex.GROUP_FAVORITE) ||
-                    pn.isMember(FilteredThreadIndex.GROUP_IGNORE))
-                    return true;
-                else
-                    return false;
-            } else {
-                // not valid, ignore
-                return false;
-            }
-        } else if ( (AddressesServlet.ACTION_UPDATE_ARCHIVE.equals(action)) || 
-             (AddressesServlet.ACTION_UPDATE_BLOG.equals(action)) ||
-             (AddressesServlet.ACTION_UPDATE_EEPSITE.equals(action)) || 
-             (AddressesServlet.ACTION_UPDATE_OTHER.equals(action)) ||
-             (AddressesServlet.ACTION_UPDATE_PEER.equals(action)) ) {
-            return updateAddress(user, req);
-        } else if (AddressesServlet.ACTION_PURGE_AND_BAN_BLOG.equals(action)) {
-            String name = req.getParameter(AddressesServlet.PARAM_NAME);
-            PetName pn = user.getPetNameDB().getByName(name);
-            if (pn != null) {
-                boolean purged = false;
-                if (BlogManager.instance().authorizeRemote(user)) {
-                    Hash h = null;
-                    BlogURI uri = new BlogURI(pn.getLocation());
-                    if (uri.getKeyHash() != null) {
-                        h = uri.getKeyHash();
-                    }
-                    if (h == null) {
-                        byte b[] = Base64.decode(pn.getLocation());
-                        if ( (b != null) && (b.length == Hash.HASH_LENGTH) )
-                            h = new Hash(b);
-                    }
-                    if (h != null) {
-                        BlogManager.instance().purgeAndBan(h);
-                        purged = true;
-                    }
-                }
-                if (purged) // force a new thread index
-                    return true;
-                else
-                    return false;
-            } else {
-                return false;
-            }
-        } else if ( (AddressesServlet.ACTION_DELETE_ARCHIVE.equals(action)) || 
-             (AddressesServlet.ACTION_DELETE_BLOG.equals(action)) ||
-             (AddressesServlet.ACTION_DELETE_EEPSITE.equals(action)) || 
-             (AddressesServlet.ACTION_DELETE_OTHER.equals(action)) ||
-             (AddressesServlet.ACTION_DELETE_TAG.equals(action)) ||
-             (AddressesServlet.ACTION_DELETE_PEER.equals(action)) ) {
-            String name = req.getParameter(AddressesServlet.PARAM_NAME);
-            PetName pn = user.getPetNameDB().getByName(name);
-            if (pn != null) {
-                user.getPetNameDB().remove(pn);
-                BlogManager.instance().saveUser(user);
-                updateSyndication(user, pn.getLocation(), false);
-                if (pn.isMember(FilteredThreadIndex.GROUP_FAVORITE) ||
-                    pn.isMember(FilteredThreadIndex.GROUP_IGNORE))
-                    return true;
-                else
-                    return false;
-            } else {
-                return false;
-            }
-        } else {
-            // not an addressbook op
-            return false;
-        }
-    }
-    
-    private boolean updateAddress(User user, HttpServletRequest req) {
-        PetName pn = user.getPetNameDB().getByName(req.getParameter(AddressesServlet.PARAM_NAME));
-        if (pn != null) {
-            boolean wasIgnored = pn.isMember(FilteredThreadIndex.GROUP_IGNORE);
-            boolean wasFavorite = pn.isMember(FilteredThreadIndex.GROUP_FAVORITE);
-            
-            pn.setIsPublic(!empty(req, AddressesServlet.PARAM_IS_PUBLIC));
-            pn.setLocation(req.getParameter(AddressesServlet.PARAM_LOC));
-            pn.setNetwork(req.getParameter(AddressesServlet.PARAM_NET));
-            pn.setProtocol(req.getParameter(AddressesServlet.PARAM_PROTO));
-            if (empty(req, AddressesServlet.PARAM_FAVORITE))
-                pn.removeGroup(FilteredThreadIndex.GROUP_FAVORITE);
-            else
-                pn.addGroup(FilteredThreadIndex.GROUP_FAVORITE);
-            if (empty(req, AddressesServlet.PARAM_IGNORE))
-                pn.removeGroup(FilteredThreadIndex.GROUP_IGNORE);
-            else
-                pn.addGroup(FilteredThreadIndex.GROUP_IGNORE);
-            
-            BlogManager.instance().saveUser(user);
-            
-            if (AddressesServlet.PROTO_ARCHIVE.equals(pn.getProtocol()))
-                updateSyndication(user, pn.getLocation(), !empty(req, AddressesServlet.PARAM_SYNDICATE));
-            
-            return (wasIgnored != pn.isMember(FilteredThreadIndex.GROUP_IGNORE)) ||
-                   (wasFavorite != pn.isMember(FilteredThreadIndex.GROUP_IGNORE));
-        } else {
-            return false;
-        }
-    }
-    
-    protected void updateSyndication(User user, String loc, boolean shouldAutomate) {
-        if (BlogManager.instance().authorizeRemote(user)) {
-            if (shouldAutomate)
-                BlogManager.instance().scheduleSyndication(loc);
-            else
-                BlogManager.instance().unscheduleSyndication(loc);
-        }
-    }
-    
-    protected PetName buildNewAddress(HttpServletRequest req) {
-        PetName pn = new PetName();
-        pn.setName(req.getParameter(AddressesServlet.PARAM_NAME));
-        pn.setIsPublic(!empty(req, AddressesServlet.PARAM_IS_PUBLIC));
-        pn.setLocation(req.getParameter(AddressesServlet.PARAM_LOC));
-        pn.setNetwork(req.getParameter(AddressesServlet.PARAM_NET));
-        pn.setProtocol(req.getParameter(AddressesServlet.PARAM_PROTO));
-        if (empty(req, AddressesServlet.PARAM_FAVORITE))
-            pn.removeGroup(FilteredThreadIndex.GROUP_FAVORITE);
-        else
-            pn.addGroup(FilteredThreadIndex.GROUP_FAVORITE);
-        if (empty(req, AddressesServlet.PARAM_IGNORE))
-            pn.removeGroup(FilteredThreadIndex.GROUP_IGNORE);
-        else
-            pn.addGroup(FilteredThreadIndex.GROUP_IGNORE);
-        return pn;
-    }
-    
-    protected void handleUpdateProfile(User user, HttpServletRequest req) {
-        if ( (user == null) || (!user.getAuthenticated()) || (user.getBlog() == null) )
-            return;
-        
-        String action = req.getParameter("action");
-        if ( (action == null) || !("Update profile".equals(action)) )
-            return;
-        
-        String name = req.getParameter(ThreadedHTMLRenderer.PARAM_PROFILE_NAME);
-        String desc = req.getParameter(ThreadedHTMLRenderer.PARAM_PROFILE_DESC);
-        String url = req.getParameter(ThreadedHTMLRenderer.PARAM_PROFILE_URL);
-        String other = req.getParameter(ThreadedHTMLRenderer.PARAM_PROFILE_OTHER);
-        
-        Properties opts = new Properties();
-        if (!empty(name))
-            opts.setProperty(BlogInfo.NAME, name.trim());
-        if (!empty(desc))
-            opts.setProperty(BlogInfo.DESCRIPTION, desc.trim());
-        if (!empty(url))
-            opts.setProperty(BlogInfo.CONTACT_URL, url.trim());
-        if (!empty(other)) {
-            StringBuffer key = new StringBuffer();
-            StringBuffer val = null;
-            for (int i = 0; i < other.length(); i++) {
-                char c = other.charAt(i);
-                if ( (c == ':') || (c == '=') ) {
-                    if (val != null) {
-                        val.append(c);
-                    } else {
-                        val = new StringBuffer();
-                    }
-                } else if ( (c == '\n') || (c == '\r') ) {
-                    String k = key.toString().trim();
-                    String v = (val != null ? val.toString().trim() : "");
-                    if ( (k.length() > 0) && (v.length() > 0) ) {
-                        opts.setProperty(k, v);
-                    }
-                    key.setLength(0);
-                    val = null;
-                } else if (val != null) {
-                    val.append(c);
-                } else {
-                    key.append(c);
-                }
-            }
-            // now finish the last of it
-            String k = key.toString().trim();
-            String v = (val != null ? val.toString().trim() : "");
-            if ( (k.length() > 0) && (v.length() > 0) ) {
-                opts.setProperty(k, v);
-            }
-        }
-        
-        String pass0 = req.getParameter("password");
-        String pass1 = req.getParameter("passwordConfirm");
-        String oldPass = req.getParameter("oldPassword");
-        
-        if ( (pass0 != null) && (pass1 != null) && (pass0.equals(pass1)) ) {
-            BlogManager.instance().changePasswrd(user, oldPass, pass0, pass1);
-        }
-            
-        if (user.getAuthenticated() && !BlogManager.instance().authorizeRemote(user)) {
-            String adminPass = req.getParameter("adminPass");
-            if (adminPass != null) {
-                boolean authorized = BlogManager.instance().authorizeRemote(adminPass);
-                if (authorized) {
-                    user.setAllowAccessRemote(authorized);
-                    BlogManager.instance().saveUser(user);
-                }
-            }
-        }
-
-        boolean updated = BlogManager.instance().updateMetadata(user, user.getBlog(), opts);
-    }
-    
-    private User handleRegister(User user, HttpServletRequest req) {
-        String l = req.getParameter("login");
-        String p = req.getParameter("password");
-        String name = req.getParameter("accountName");
-        String desc = req.getParameter("description");
-        String contactURL = req.getParameter("url");
-        String regPass = req.getParameter("registrationPass");
-        String action = req.getParameter("action");
-        
-        if ( (action != null) && ("Register".equals(action)) && !empty(l) ) {
-            return BlogManager.instance().register(l, p, regPass, name, desc, contactURL);
-        } else {
-            return user;
-        }
-    }
-    
-    private void handleAdmin(User user, HttpServletRequest req) throws IOException {
-        if (BlogManager.instance().authorizeRemote(user)) {
-            String action = req.getParameter("action");
-            if ( (action != null) && ("Save config".equals(action)) ) {
-                boolean wantSingle = !empty(req, "singleuser");
-                String defaultUser = req.getParameter("defaultUser");
-                String defaultPass = req.getParameter("defaultPass");
-                String regPass = req.getParameter("regpass");
-                String remotePass = req.getParameter("remotepass");
-                String proxyHost = req.getParameter("proxyhost");
-                String proxyPort = req.getParameter("proxyport");
-                
-                // default user cannot be empty, but the rest can be blank
-                if ( (!empty(defaultUser)) && (defaultPass != null) && (regPass != null) && (remotePass != null) && 
-                     (proxyHost != null) && (proxyPort != null) ) {
-                    int port = 4444;
-                    try { port = Integer.parseInt(proxyPort); } catch (NumberFormatException nfe) {}
-                    BlogManager.instance().configure(regPass, remotePass, null, null, proxyHost, port, wantSingle, 
-                                                     null, defaultUser, defaultPass);
-                }
-            }
-        }
-    }
-        
-    
-    protected void render(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws ServletException, IOException {
-        Archive archive = BlogManager.instance().getArchive();
-        int numThreads = 10;
-        int threadOffset = getOffset(req);
-        if (threadOffset == -1) {
-            threadOffset = index.getRootCount() - numThreads;
-        } 
-        if (threadOffset < 0) {
-            threadOffset = 0;
-        }
-
-        BlogURI visibleEntry = getVisible(req);
-        
-        int offset = 0;
-        if ( empty(req, ThreadedHTMLRenderer.PARAM_OFFSET) && (visibleEntry != null) ) {
-            // we're on a permalink, so jump the tree to the given thread
-            threadOffset = index.getRoot(visibleEntry);
-            if (threadOffset < 0)
-                threadOffset = 0;
-        }
-        
-        renderBegin(user, req, out, index);
-        renderNavBar(user, req, out);
-        renderControlBar(user, req, out, index);
-        renderServletDetails(user, req, out, index, threadOffset, visibleEntry, archive);
-        renderEnd(user, req, out, index);
-    }
-    
-    protected void renderBegin(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException {
-        out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
-        out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
-        out.write("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n<head>\n<title>" + getTitle() + "</title>\n");
-        out.write("<meta http-equiv=\"cache-control\" content=\"no-cache\" />\n");
-        out.write("<meta http-equiv=\"pragma\" content=\"no-cache\" />\n");
-        out.write("<style>\n");
-        out.write(STYLE_HTML);
-        Reader css = null;
-        try {
-            InputStream in = req.getSession().getServletContext().getResourceAsStream("/syndie.css");
-            if (in != null) {
-                css = new InputStreamReader(in, "UTF-8");
-                char buf[] = new char[1024];
-                int read = 0;
-                while ( (read = css.read(buf)) != -1) 
-                    out.write(buf, 0, read);
-            }
-        } finally {
-            if (css != null)
-                css.close();
-        }
-        String content = FileUtil.readTextFile("./docs/syndie_standard.css", -1, true); 
-        if (content != null) out.write(content);
-        out.write("</style>\n");
-        out.write(BEGIN_HTML);
-    }
-    
-    protected void renderNavBar(User user, HttpServletRequest req, PrintWriter out) throws IOException {
-        out.write("<div class=\"syndieBlogTopNav\">\n");
-        out.write("<span class=\"syndieBlogTopNavUser\">\n");
-        out.write("<!-- nav bar begin -->\n");
-        out.write("<a href=\"threads.jsp\" title=\"Syndie home\">Threads</a> <a href=\"blogs.jsp\" title=\"Blog summary\">Blogs</a> ");
-        if (user.getAuthenticated() && (user.getBlog() != null) ) {
-            out.write("Logged in as <a href=\"" + getProfileLink(req, user.getBlog()) + "\" title=\"Edit your profile\">");
-            out.write(user.getUsername());
-            out.write("</a>\n");
-            out.write("(<a href=\"switchuser.jsp\" title=\"Log in as another user\">switch</a>)\n");
-            out.write("<a href=\"" + getPostURI() + "\" title=\"Post a new thread\">Post</a>\n");
-            out.write("<a href=\"addresses.jsp\" title=\"View your addressbook\">Addressbook</a>\n");
-        } else {
-            out.write("<a href=\"switchuser.jsp\">Log in</a>\n");
-        }
-        out.write("</span><span class=\"syndieBlogTopNavAdmin\">\n");
-        out.write("<a href=\"about.html\" title=\"Basic Syndie info\">About</a> ");
-        if (BlogManager.instance().authorizeRemote(user)) {
-            out.write("<a href=\"" + getSyndicateLink(user, null) + "\" title=\"Syndicate data between other Syndie nodes\">Syndicate</a>\n");
-            out.write("<a href=\"importfeed.jsp\" title=\"Import RSS/Atom data\">Import RSS/Atom</a>\n");
-            out.write("<a href=\"admin.jsp\" title=\"Configure this Syndie node\">Admin</a>\n");
-        }
-        out.write("</span><!-- nav bar end -->\n</div>\n");
-    }
-    
-    /*
-    
-    protected void renderNavBar(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException {
-        //out.write("<tr class=\"topNav\"><td class=\"topNav_user\" colspan=\"2\" nowrap=\"true\">\n");
-        out.write("<tr class=\"topNav\"><td colspan=\"3\" nowrap=\"true\"><span class=\"topNav_user\">\n");
-        out.write("<!-- nav bar begin -->\n");
-        out.write("<a href=\"threads.jsp\" title=\"Syndie home\">Threads</a> <a href=\"blogs.jsp\" title=\"Blog summary\">Blogs</a> ");
-        if (user.getAuthenticated() && (user.getBlog() != null) ) {
-            out.write("Logged in as <a href=\"" + getProfileLink(req, user.getBlog()) + "\" title=\"Edit your profile\">");
-            out.write(user.getUsername());
-            out.write("</a>\n");
-            out.write("(<a href=\"switchuser.jsp\" title=\"Log in as another user\">switch</a>)\n");
-            out.write("<a href=\"" + getPostURI() + "\" title=\"Post a new thread\">Post</a>\n");
-            out.write("<a href=\"addresses.jsp\" title=\"View your addressbook\">Addressbook</a>\n");
-        } else {
-            out.write("<form action=\"" + req.getRequestURI() + "\" method=\"POST\">\n");
-            writeAuthActionFields(out);
-            out.write("Login: <input type=\"text\" name=\"login\" title=\"Login name for your Syndie account\" />\n");
-            out.write("Password: <input type=\"password\" name=\"password\" title=\"Password to get into your Syndie account\" />\n");
-            out.write("<input type=\"submit\" name=\"action\" value=\"Login\" /></form>\n");
-        }
-        //out.write("</td><td class=\"topNav_admin\">\n");
-        out.write("</span><span class=\"topNav_admin\">\n");
-        out.write("<a href=\"about.html\" title=\"Basic Syndie info\">About</a> ");
-        if (BlogManager.instance().authorizeRemote(user)) {
-            out.write("<a href=\"" + getSyndicateLink(user, null) + "\" title=\"Syndicate data between other Syndie nodes\">Syndicate</a>\n");
-            out.write("<a href=\"importfeed.jsp\" title=\"Import RSS/Atom data\">Import RSS/Atom</a>\n");
-            out.write("<a href=\"admin.jsp\" title=\"Configure this Syndie node\">Admin</a>\n");
-        }
-        out.write("</span><!-- nav bar end -->\n</td></tr>\n");
-    }
-     */
-    
-    protected String getSyndicateLink(User user, String location) { 
-        if (location != null)
-            return "syndicate.jsp?" + SyndicateServlet.PARAM_LOCATION + "=" + location;
-        return "syndicate.jsp";
-    }
-    
-    protected static final ArrayList SKIP_TAGS = new ArrayList();
-    static {
-        SKIP_TAGS.add("action");
-        SKIP_TAGS.add("filter");
-        // post and visible are skipped since we aren't good at filtering by tag when the offset will
-        // skip around randomly.  at least, not yet.
-        SKIP_TAGS.add(ThreadedHTMLRenderer.PARAM_VISIBLE);
-        //SKIP_TAGS.add("post");
-        //SKIP_TAGS.add("thread");
-        SKIP_TAGS.add(ThreadedHTMLRenderer.PARAM_OFFSET); // if we are adjusting the filter, ignore the previous offset
-        SKIP_TAGS.add(ThreadedHTMLRenderer.PARAM_DAYS_BACK);
-        SKIP_TAGS.add("addLocation");
-        SKIP_TAGS.add("addGroup");
-        SKIP_TAGS.add("login");
-        SKIP_TAGS.add("password");
-    }
-    
-    private static final String CONTROL_TARGET = "threads.jsp";
-    protected String getControlTarget() { return CONTROL_TARGET; }
-    protected String getPostURI() { return "post.jsp"; }
-    
-    protected void renderControlBar(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException {
-        out.write("<form action=\"");
-        //out.write(req.getRequestURI());
-        out.write(getControlTarget());
-        out.write("\" method=\"GET\">\n");
-        String tags = "";
-        String author = "";
-        Enumeration params = req.getParameterNames();
-        while (params.hasMoreElements()) {
-            String param = (String)params.nextElement();
-            String val = req.getParameter(param);
-            if (ThreadedHTMLRenderer.PARAM_TAGS.equals(param)) {
-                tags = val;
-            } else if (ThreadedHTMLRenderer.PARAM_AUTHOR.equals(param)) {
-                author = val;
-            } else if (SKIP_TAGS.contains(param)) {
-                // skip
-            } else if (param.length() <= 0) {
-                // skip
-            } else {
-                out.write("<input type=\"hidden\" name=\"" + param + "\" value=\"" + val + "\" />\n");
-            }
-        }
-        out.write("<tr class=\"controlBar\"><td colspan=\"2\" width=\"99%\">\n");
-        out.write("<!-- control bar begin -->\n");
-        out.write("Filter: <select name=\"" + ThreadedHTMLRenderer.PARAM_AUTHOR + "\">\n");
-        
-        PetNameDB db = user.getPetNameDB();
-        TreeSet names = new TreeSet(db.getNames());
-        out.write("<option value=\"\">Any authors</option>\n");
-        if (user.getAuthenticated()) {
-            if ("favorites".equals(author))
-                out.write("<option selected=\"true\" value=\"favorites\">All recent posts by favorite authors</option>\n");
-            else
-                out.write("<option value=\"favorites\">All recent posts by favorite authors</option>\n");
-        }
-        if (user.getBlog() != null) {
-            if ( (author != null) && (author.equals(user.getBlog().toBase64())) )
-                out.write("<option value=\"" + user.getBlog().toBase64() + "\" selected=\"true\">Threads you posted in</option>\n");
-            else
-                out.write("<option value=\"" + user.getBlog().toBase64() + "\">Threads you posted in</option>\n");
-        }
-        
-        for (Iterator iter = names.iterator(); iter.hasNext(); ) {
-            String name = (String) iter.next();
-            PetName pn = db.getByName(name);
-            if (pn == null)
-                continue;
-            String proto = pn.getProtocol();
-            String loc = pn.getLocation();
-            if (proto != null && loc != null && "syndieblog".equals(proto) && pn.isMember(FilteredThreadIndex.GROUP_FAVORITE)) {
-                if ( (author != null) && (author.equals(pn.getLocation())) )
-                    out.write("<option value=\"" + loc + "\" selected=\"true\">Threads " + name + " posted in</option>\n");
-                else
-                    out.write("<option value=\"" + loc + "\">Threads " + name + " posted in</option>\n");
-            }
-        }
-        out.write("</select>\n");
-        
-        out.write("Tags: ");
-        writeTagField(user, tags, out);
-
-        String days = req.getParameter(ThreadedHTMLRenderer.PARAM_DAYS_BACK);
-        if (days == null)
-            days = "";
-        out.write("Age: <input type=\"text\" name=\"" + ThreadedHTMLRenderer.PARAM_DAYS_BACK + "\" size=\"2\" value=\"" + days
-                  + "\" title=\"Posts are filtered to include only ones which were made within this many days ago\" /> days\n");
-        
-        out.write("<input type=\"submit\" name=\"action\" value=\"Go\" />\n");
-        out.write("</td><td class=\"controlBarRight\" width=\"1%\">");
-        
-        if ( (req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST) != null) ||
-             (req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD) != null) )
-            out.write("<a href=\"#threads\" title=\"Jump to the thread navigation\">Threads</a>");
-        out.write("</td>\n");
-        out.write("<!-- control bar end -->\n");
-        out.write("</tr>\n");
-        out.write("</form>\n");
-    }
-    
-    protected void writeTagField(User user, String selectedTags, PrintWriter out) throws IOException {
-        writeTagField(user, selectedTags, out, "Threads are filtered to include only ones with posts containing these tags", "Any tags - no filtering", true);
-    }
-    public static void writeTagField(User user, String selectedTags, Writer out, String title, String blankTitle, boolean includeFavoritesTag) throws IOException {
-        Set favoriteTags = new TreeSet(user.getFavoriteTags());
-        if (favoriteTags.size() <= 0) {
-            out.write("<input type=\"text\" name=\"" + ThreadedHTMLRenderer.PARAM_TAGS + "\" size=\"10\" value=\"" + selectedTags
-                      + "\" title=\"" + title + "\" />\n");
-        } else {
-            out.write("<select name=\"" + ThreadedHTMLRenderer.PARAM_TAGS 
-                      + "\" title=\"" + title + "\">");
-            out.write("<option value=\"\">" + blankTitle + "</option>\n");
-            boolean matchFound = false;
-            if (includeFavoritesTag) {
-                out.write("<option value=\"");
-                StringBuffer combinedBuf = new StringBuffer();
-                for (Iterator iter = favoriteTags.iterator(); iter.hasNext(); ) {
-                    String curFavTag = (String)iter.next();
-                    combinedBuf.append(HTMLRenderer.sanitizeTagParam(curFavTag)).append(" ");
-                }
-                String combined = combinedBuf.toString();
-                if (selectedTags.equals(combined)) {
-                    out.write(combined + "\" selected=\"true\" >All favorite tags</option>\n");
-                    matchFound = true;
-                } else {
-                    out.write(combined + "\" >All favorite tags</option>\n");
-                }
-            }
-            
-            for (Iterator iter = favoriteTags.iterator(); iter.hasNext(); ) {
-                String curFavTag = (String)iter.next();
-                if (selectedTags.equals(curFavTag)) {
-                    out.write("<option value=\"" + HTMLRenderer.sanitizeTagParam(curFavTag) + "\" selected=\"true\" >" 
-                              + HTMLRenderer.sanitizeString(curFavTag)  + "</option>\n");
-                    matchFound = true;
-                } else {
-                    out.write("<option value=\"" + HTMLRenderer.sanitizeTagParam(curFavTag) + "\">" 
-                              + HTMLRenderer.sanitizeString(curFavTag)  + "</option>\n");
-                }
-            }
-            if ( (!matchFound) && (selectedTags != null) && (selectedTags.trim().length() > 0) )
-                out.write("<option value=\"" + HTMLRenderer.sanitizeTagParam(selectedTags) 
-                          + "\" selected=\"true\">" + HTMLRenderer.sanitizeString(selectedTags) + "</option>\n");
-            
-            out.write("</select>\n");
-        }
-    }
-   
-    protected abstract void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, 
-                                                 ThreadIndex index, int threadOffset, BlogURI visibleEntry, 
-                                                 Archive archive) throws IOException;
-    
-    protected static final int getOffset(HttpServletRequest req) {
-        String off = req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET);
-        try {
-            return Integer.parseInt(off);
-        } catch (NumberFormatException nfe) {
-            return 0;
-        }
-    }
-    protected static final BlogURI getVisible(HttpServletRequest req) {
-        return getAsBlogURI(req.getParameter(ThreadedHTMLRenderer.PARAM_VISIBLE));
-    }
-    protected static final BlogURI getAsBlogURI(String uri) {
-        if (uri != null) {
-            int split = uri.indexOf('/');
-            if ( (split <= 0) || (split + 1 >= uri.length()) )
-                return null;
-            String blog = uri.substring(0, split);
-            String id = uri.substring(split+1);
-            try {
-                Hash hash = new Hash();
-                hash.fromBase64(blog);
-                long msgId = Long.parseLong(id);
-                if (msgId > 0)
-                    return new BlogURI(hash, msgId);
-            } catch (DataFormatException dfe) {
-                return null;
-            } catch (NumberFormatException nfe) {
-                return null;
-            }
-        }
-        return null;
-    }
-    
-
-    protected String trim(String orig, int maxLen) {
-        if ( (orig == null) || (orig.length() <= maxLen) )
-            return orig;
-        return orig.substring(0, maxLen) + "...";
-    }
-    
-    protected static final boolean empty(HttpServletRequest req, String param) {
-        String val = req.getParameter(param);
-        return (val == null) || (val.trim().length() <= 0);
-    }
-    
-    protected static final boolean empty(String val) {
-        return (val == null) || (val.trim().length() <= 0);
-    }
-    
-    protected String getExpandLink(HttpServletRequest req, ThreadNode node) {
-        return getExpandLink(node, req.getRequestURI(), req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST), 
-                             req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD), 
-                             req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
-                             req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
-                             req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
-    }
-    protected static String getExpandLink(ThreadNode node, String uri, String viewPost, String viewThread, 
-                                        String offset, String tags, String author) {
-        StringBuffer buf = new StringBuffer(64);
-        buf.append(uri);
-        buf.append('?');
-        // expand node == let one of node's children be visible
-        if (node.getChildCount() > 0) {
-            ThreadNode child = node.getChild(0);
-            buf.append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=');
-            buf.append(child.getEntry().getKeyHash().toBase64()).append('/');
-            buf.append(child.getEntry().getEntryId()).append("&amp;");
-        }
-        
-        if (!empty(viewPost))
-            buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(viewPost).append("&amp;");
-        else if (!empty(viewThread))
-            buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=').append(viewThread).append("&amp;");
-        
-        if (!empty(offset))
-            buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append("&amp;");
-        
-        if (!empty(tags)) 
-            buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append("&amp;");
-        
-        if (!empty(author)) 
-            buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(author).append("&amp;");
-        
-        return buf.toString();
-    }
-    protected String getCollapseLink(HttpServletRequest req, ThreadNode node) {
-        return getCollapseLink(node, req.getRequestURI(), 
-                               req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST),
-                               req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD),
-                               req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
-                               req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
-                               req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
-    }
-
-    protected String getCollapseLink(ThreadNode node, String uri, String viewPost, String viewThread, 
-                                   String offset, String tags, String author) { 
-        StringBuffer buf = new StringBuffer(64);
-        buf.append(uri);
-        // collapse node == let the node be visible
-        buf.append('?').append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=');
-        buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
-        buf.append(node.getEntry().getEntryId()).append("&amp;");
-
-        if (!empty(viewPost))
-            buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(viewPost).append("&amp;");
-        else if (!empty(viewThread))
-            buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=').append(viewThread).append("&amp;");
-        
-        if (!empty(offset))
-            buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append("&amp;");
-        
-        if (!empty(tags))
-            buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append("&amp;");
-        
-        if (!empty(author))
-            buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(author).append("&amp;");
-        
-        return buf.toString();
-    }
-    protected String getProfileLink(HttpServletRequest req, Hash author) {
-        return getProfileLink(author);
-    }
-    protected String getProfileLink(Hash author) { return ThreadedHTMLRenderer.buildProfileURL(author); }
-    
-    protected String getAddToGroupLink(HttpServletRequest req, Hash author, User user, String group) {
-        return getAddToGroupLink(user, author, group, req.getRequestURI(), 
-                                 req.getParameter(ThreadedHTMLRenderer.PARAM_VISIBLE),
-                                 req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST), 
-                                 req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD),
-                                 req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET), 
-                                 req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS), 
-                                 req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
-    }
-    protected String getAddToGroupLink(User user, Hash author, String group, String uri, String visible,
-                                     String viewPost, String viewThread, String offset, String tags, String filteredAuthor) {
-        StringBuffer buf = new StringBuffer(64);
-        buf.append(uri);
-        buf.append('?');
-        if (!empty(visible))
-            buf.append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=').append(visible).append("&amp;");
-        buf.append(ThreadedHTMLRenderer.PARAM_ADD_TO_GROUP_LOCATION).append('=').append(author.toBase64()).append("&amp;");
-        buf.append(ThreadedHTMLRenderer.PARAM_ADD_TO_GROUP_NAME).append('=').append(group).append("&amp;");
-
-        if (!empty(viewPost))
-            buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(viewPost).append("&amp;");
-        else if (!empty(viewThread))
-            buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=').append(viewThread).append("&amp;");
-        
-        if (!empty(offset))
-            buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append("&amp;");
-
-        if (!empty(tags))
-            buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append("&amp;");
-        
-        if (!empty(filteredAuthor))
-            buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(filteredAuthor).append("&amp;");
-        
-        addAuthActionParams(buf);
-        return buf.toString();
-    }
-    protected String getRemoveFromGroupLink(User user, String name, String group, String uri, String visible,
-                                            String viewPost, String viewThread, String offset, String tags, String filteredAuthor) {
-        StringBuffer buf = new StringBuffer(64);
-        buf.append(uri);
-        buf.append('?');
-        if (!empty(visible))
-            buf.append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=').append(visible).append("&amp;");
-        buf.append(ThreadedHTMLRenderer.PARAM_REMOVE_FROM_GROUP_NAME).append('=').append(name).append("&amp;");
-        buf.append(ThreadedHTMLRenderer.PARAM_REMOVE_FROM_GROUP).append('=').append(group).append("&amp;");
-
-        if (!empty(viewPost))
-            buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(viewPost).append("&amp;");
-        else if (!empty(viewThread))
-            buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=').append(viewThread).append("&amp;");
-        
-        if (!empty(offset))
-            buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append("&amp;");
-
-        if (!empty(tags))
-            buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append("&amp;");
-        
-        if (!empty(filteredAuthor))
-            buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(filteredAuthor).append("&amp;");
-        
-        addAuthActionParams(buf);
-        return buf.toString();
-    }
-    protected String getViewPostLink(HttpServletRequest req, ThreadNode node, User user, boolean isPermalink) {
-        return ThreadedHTMLRenderer.getViewPostLink(req.getRequestURI(), node, user, isPermalink, 
-                                                    req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET), 
-                                                    req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS), 
-                                                    req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR),
-                                                    Boolean.valueOf(req.getParameter(ThreadedHTMLRenderer.PARAM_THREAD_AUTHOR)).booleanValue());
-    }
-    protected String getViewPostLink(HttpServletRequest req, BlogURI post, User user) {
-        return ThreadedHTMLRenderer.getViewPostLink(req.getRequestURI(), post, user, false, 
-                                                    req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET), 
-                                                    req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS), 
-                                                    req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR),
-                                                    Boolean.valueOf(req.getParameter(ThreadedHTMLRenderer.PARAM_THREAD_AUTHOR)).booleanValue());
-    }
-    protected String getViewThreadLink(HttpServletRequest req, ThreadNode node, User user) {
-        return getViewThreadLink(req.getRequestURI(), node, user,
-                                 req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
-                                 req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
-                                 req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR),
-                                 Boolean.valueOf(req.getParameter(ThreadedHTMLRenderer.PARAM_THREAD_AUTHOR)).booleanValue());
-    }
-    protected static String getViewThreadLink(String uri, ThreadNode node, User user, String offset,
-                                            String tags, String author, boolean authorOnly) {
-        StringBuffer buf = new StringBuffer(64);
-        buf.append(uri);
-        BlogURI expandTo = node.getEntry();
-        if (node.getChildCount() > 0) {
-            if (true) {
-                // lets expand to the leaf
-                expandTo = new BlogURI(node.getMostRecentPostAuthor(), node.getMostRecentPostDate());
-            } else {
-                // only expand one level
-                expandTo = node.getChild(0).getEntry();
-            }
-        } 
-        buf.append('?').append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=');
-        buf.append(expandTo.getKeyHash().toBase64()).append('/');
-        buf.append(expandTo.getEntryId()).append("&amp;");
-        
-        buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=');
-        buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
-        buf.append(node.getEntry().getEntryId()).append("&amp;");
-        
-        if (!empty(offset))
-            buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append("&amp;");
-        
-        if (!empty(tags))
-            buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append("&amp;");
-        
-        if (!empty(author)) {
-            buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(author).append("&amp;");
-            if (authorOnly)
-                buf.append(ThreadedHTMLRenderer.PARAM_THREAD_AUTHOR).append("=true&amp;");
-        }
-        buf.append("#").append(node.getEntry().toString());
-        return buf.toString();
-    }
-    protected String getFilterByTagLink(HttpServletRequest req, ThreadNode node, User user, String tag, String author) { 
-        return ThreadedHTMLRenderer.getFilterByTagLink(req.getRequestURI(), node, user, tag, author);
-    }
-    protected String getNavLink(HttpServletRequest req, int offset) {
-        return ThreadedHTMLRenderer.getNavLink(req.getRequestURI(),
-                                               req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST), 
-                                               req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD), 
-                                               req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS), 
-                                               req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR), 
-                                               Boolean.valueOf(req.getParameter(ThreadedHTMLRenderer.PARAM_THREAD_AUTHOR)).booleanValue(), 
-                                               offset);
-    }
-    
-    protected void renderEnd(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException {
-        out.write(END_HTML);
-    }
-
-    protected Collection getFilteredTags(HttpServletRequest req) {
-        String tags = req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS);
-        if (tags != null) {
-            StringTokenizer tok = new StringTokenizer(tags, "\n\t ");
-            ArrayList rv = new ArrayList();
-            while (tok.hasMoreTokens()) {
-                String tag = tok.nextToken().trim();
-                if (tag.length() > 0)
-                    rv.add(tag);
-            }
-            return rv;
-        } else {
-            return Collections.EMPTY_LIST;
-        }
-    }
-    
-    protected Collection getFilteredAuthors(HttpServletRequest req) {
-        List rv = new ArrayList();
-        rv.addAll(getAuthors(req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR)));
-        //rv.addAll(getAuthors(req.getParameter(ThreadedHTMLRenderer.PARAM_THREAD_AUTHOR)));
-        return rv;
-    }
-    
-    private Collection getAuthors(String authors) {
-        if (authors != null) {
-            StringTokenizer tok = new StringTokenizer(authors, "\n\t ");
-            ArrayList rv = new ArrayList();
-            while (tok.hasMoreTokens()) {
-                try {
-                    Hash h = new Hash();
-                    h.fromBase64(tok.nextToken().trim());
-                    rv.add(h);
-                } catch (DataFormatException dfe) {}
-            }
-            return rv;
-        } else {
-            return Collections.EMPTY_LIST;
-        }
-    }
-    
-    private static final String BEGIN_HTML = "<link href=\"rss.jsp\" rel=\"alternate\" type=\"application/rss+xml\" >\n" +
-"</head>\n" +
-"<body>\n" +
-"<span style=\"display: none\"><a href=\"#bodySubject\">Jump to the beginning of the first post rendered, if any</a>\n" +
-"<a href=\"#threads\">Jump to the thread navigation</a>\n</span>\n" +
-"<table border=\"0\" width=\"100%\" class=\"overallTable\">\n";
-    private static final String STYLE_HTML = "* {\n" +
-"    margin: 0;\n" +
-"    padding: 0;\n" +
-"}\n" +
-"body {\n" +
-"   font-family: Arial, Helvetica, sans-serif;\n" +
-"    font-size: 100%;\n" +
-"   background-color : #EEEEEE;\n" +
-"   color: #000000;\n" +
-"}\n" +
-"select {\n" +
-"    min-width: 1.5em;\n" +
-"}\n" +
-".overallTable {\n" +
-"    border-spacing: 0px;\n" +
-"    border-collapse:collapse;\n" +
-"    float:left;\n" +
-"}\n" +
-".topNav {\n" +
-"   background-color: #BBBBBB;\n" +
-"}\n" +
-".topNav_user {\n" +
-"   text-align: left;\n" +
-"   float: left;\n" +
-"   display: inline;\n" +
-"}\n" +
-".topNav_admin {\n" +
-"   text-align: right;\n" +
-"   float: right;\n" +
-"   margin: 0 5px 0 0;\n" +
-"   display: inline;\n" +
-"}\n" +
-".controlBar {\n" +
-"   background-color: #BBBBBB;\n" +
-"}\n" +
-".controlBarRight {\n" +
-"   text-align: right;\n" +
-"}\n" +
-".threadEven {\n" +
-"   background-color: #FFFFFF;\n" +
-"   white-space: nowrap;\n" +
-"}\n" +
-".threadOdd {\n" +
-"   background-color: #EEEEEE;\n" +
-"   white-space: nowrap;\n" +
-"}\n" +
-".threadLeft {\n" +
-"   text-align: left;\n" +
-"   align: left;\n" +
-"}\n" +
-".threadNav {\n" +
-"   background-color: #BBBBBB;\n" +
-"}\n" +
-".threadNavRight {\n" +
-"   text-align: right;\n" +
-"   float: right;\n" +
-"   background-color: #BBBBBB;\n" +
-"}\n" +
-".rightOffset {\n" +
-"    float: right;\n" +
-"    margin: 0 5px 0 0;\n" +
-"   display: inline;\n" +
-"}\n" +
-".threadInfoLeft {\n" +
-"    float: left;\n" +
-"    margin: 5px 0px 0 0;\n" +
-"   display: inline;\n" +
-"}\n" +
-".threadInfoRight {\n" +
-"    float: right;\n" +
-"    margin: 0 5px 0 0;\n" +
-"   display: inline;\n" +
-"}\n" +
-".postMeta {\n" +
-"   background-color: #BBBBFF;\n" +
-"}\n" +
-".postMetaSubject {\n" +
-"   text-align: left;\n" +
-"}\n" +
-".postMetaLink {\n" +
-"   text-align: right;\n" +
-"}\n" +
-".postDetails {\n" +
-"   background-color: #DDDDFF;\n" +
-"}\n" +
-".postReply {\n" +
-"   background-color: #BBBBFF;\n" +
-"}\n" +
-".postReplyText {\n" +
-"   background-color: #BBBBFF;\n" +
-"}\n" +
-".postReplyOptions {\n" +
-"   background-color: #BBBBFF;\n" +
-"}\n" +
-".syndieBlogTopNav {\n" +
-"    float:left;\n" +
-"    width: 100%;\n" +
-"    background-color: #BBBBBB;\n" +
-"}\n" +
-".syndieBlogTopNavUser {\n" +
-"    text-align: left;\n" +
-"    float: left;\n" +
-"}\n" +
-".syndieBlogTopNavAdmin {\n" +
-"    text-align: left;\n" +
-"    float: right;\n" +
-"}\n" +
-".syndieBlogFavorites {\n" +
-"    float: left;\n" +
-"    margin: 5px 0px 0 0;\n" +
-"   display: inline;\n" +
-"}\n" +
-".syndieBlogList {\n" +
-"    float: right;\n" +
-"    margin: 5px 0px 0 0;\n" +
-"   display: inline;\n" +
-"}\n";
-    
-    private static final String END_HTML = "</table>\n" +
-"</body>\n";
-   
-    protected String getTitle() { return "Syndie"; }
-    
-    protected static class TreeRenderState {
-        private int _rowsWritten;
-        private int _rowsSkipped;
-        private List _ignored;
-        public TreeRenderState(List ignored) { 
-            _rowsWritten = 0; 
-            _rowsSkipped = 0;
-            _ignored = ignored;
-        }
-        public int getRowsWritten() { return _rowsWritten; }
-        public void incrementRowsWritten() { _rowsWritten++; }
-        public int getRowsSkipped() { return _rowsSkipped; }
-        public void incrementRowsSkipped() { _rowsSkipped++; }
-        public List getIgnoredAuthors() { return _ignored; }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/BlogConfigBean.java b/apps/syndie/java/src/net/i2p/syndie/web/BlogConfigBean.java
deleted file mode 100644
index 1652a525ae796f9ea3d580d9859d87840e8bc7bf..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/BlogConfigBean.java
+++ /dev/null
@@ -1,308 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Properties;
-
-import net.i2p.I2PAppContext;
-import net.i2p.client.naming.PetName;
-import net.i2p.data.DataHelper;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogInfoData;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.util.Log;
-
-/**
- *
- */
-public class BlogConfigBean {
-    private I2PAppContext _context;
-    private Log _log;
-    private User _user;
-    private String _title;
-    private String _description;
-    private String _contactInfo;
-    /** list of list of PetNames */
-    private List _groups;
-    private Properties _styleOverrides;
-    private File _logo;
-    private boolean _loaded;
-    private boolean _updated;
-    
-    public BlogConfigBean() { 
-        _context = I2PAppContext.getGlobalContext();
-        _log = _context.logManager().getLog(BlogConfigBean.class);
-        _groups = new ArrayList();
-        _styleOverrides = new Properties();
-    }
-    
-    public boolean isUpdated() { return _updated; }
-    
-    public User getUser() { return _user; }
-    public void setUser(User user) { 
-        _user = user;
-        _title = null;
-        _description = null;
-        _contactInfo = null;
-        _groups.clear();
-        _styleOverrides.clear();
-        if (_logo != null)
-            _logo.delete();
-        _logo = null;
-        _loaded = false;
-        _updated = false;
-        load();
-    }
-    public String getTitle() { return _title; }
-    public void setTitle(String title) { 
-        _title = title; 
-        _updated = true;
-    }
-    public String getDescription() { return _description; }
-    public void setDescription(String desc) { 
-        _description = desc; 
-        _updated = true;
-    }
-    public String getContactInfo() { return _contactInfo; }
-    public void setContactInfo(String info) { 
-        _contactInfo = info; 
-        _updated = true;
-    }
-    public int getGroupCount() { return _groups.size(); }
-    /** gets the actual modifiable list of PetName instances */
-    public List getGroup(int i) { return (List)_groups.get(i); }
-    /** gets the actual modifiable list of PetName instances */
-    public List getGroup(String name) {
-        for (int i = 0; i < _groups.size(); i++) {
-            List grp = (List)_groups.get(i);
-            if (grp.size() > 0) {
-                PetName pn = (PetName)grp.get(0);
-                if ( (pn.getGroupCount() == 0) && ( (name == null) || (name.length() <= 0) ) )
-                    return grp;
-                if (pn.getGroupCount() == 0)
-                    continue;
-                String curGroup = pn.getGroup(0);
-                if (curGroup.equals(name))
-                    return grp;
-            }
-        }
-        return null;
-    }
-    /** adds the given element to the appropriate group (creating a new one if necessary) */
-    public void add(PetName pn) {
-        String groupName = null;
-        if (pn.getGroupCount() > 0)
-            groupName = pn.getGroup(0);
-        List group = getGroup(groupName);
-        if (group == null) {
-            group = new ArrayList(4);
-            group.add(pn);
-            _groups.add(group);
-        } else {
-            group.add(pn);
-        }
-        _updated = true;
-    }
-    public void remove(PetName pn) {
-        String groupName = null;
-        if (pn.getGroupCount() > 0)
-            groupName = pn.getGroup(0);
-        List group = getGroup(groupName);
-        if (group != null) {
-            group.remove(pn);
-            if (group.size() <= 0)
-                _groups.remove(group);
-        }
-        _updated = true;
-    }
-    public void remove(String name) {
-        for (int i = 0; i < getGroupCount(); i++) {
-            List group = getGroup(i);
-            for (int j = 0; j < group.size(); j++) {
-                PetName pn = (PetName)group.get(j);
-                if (pn.getName().equals(name)) {
-                    group.remove(j);
-                    if (group.size() <= 0)
-                        _groups.remove(group);
-                    _updated = true;
-                    return;
-                }
-            }
-        }
-    }
-    /** take note that the groups have been updated in some way (reordered, etc) */
-    public void groupsUpdated() { _updated = true; }
-    public String getStyleOverride(String prop) { return _styleOverrides.getProperty(prop); }
-    public void setStyleOverride(String prop, String val) { 
-        _styleOverrides.setProperty(prop, val); 
-        _updated = true;
-    }
-    public void unsetStyleOverride(String prop) { 
-        _styleOverrides.remove(prop); 
-        _updated = true;
-    }
-    public File getLogo() { return _logo; }
-    public void setLogo(File logo) { 
-        if ( (logo != null) && (logo.length() > BlogInfoData.MAX_LOGO_SIZE) ) {
-            _log.error("Refusing a logo of size " + logo.length());
-            logo.delete();
-            return;
-        }
-        if (_logo != null)
-            _logo.delete();
-        _logo = logo; 
-        _updated = true; 
-    }
-    public boolean hasPendingChanges() { return _loaded && _updated; }
-    
-    private void load() {
-        Archive archive = BlogManager.instance().getArchive();
-        BlogInfo info = archive.getBlogInfo(_user.getBlog());
-        if (info != null) {
-            _title = info.getProperty(BlogInfo.NAME);
-            _description = info.getProperty(BlogInfo.DESCRIPTION);
-            _contactInfo = info.getProperty(BlogInfo.CONTACT_URL);
-            String id = info.getProperty(BlogInfo.SUMMARY_ENTRY_ID);
-            if (id != null) {
-                BlogURI uri = new BlogURI(id);
-                EntryContainer entry = archive.getEntry(uri);
-                if (entry != null) {
-                    BlogInfoData data = new BlogInfoData();
-                    try {
-                        data.load(entry);
-                        if (data.isLogoSpecified()) {
-                            File logo = File.createTempFile("logo", ".png", BlogManager.instance().getTempDir());
-                            FileOutputStream os = null;
-                            try {
-                                os = new FileOutputStream(logo);
-                                data.writeLogo(os);
-                                _logo = logo;
-                            } finally {
-                                if (os != null) try { os.close(); } catch (IOException ioe) {}
-                            }
-                        }
-                        for (int i = 0; i < data.getReferenceGroupCount(); i++) {
-                            List group = (List)data.getReferenceGroup(i);
-                            for (int j = 0; j < group.size(); j++) {
-                                PetName pn = (PetName)group.get(j);
-                                add(pn);
-                            }
-                        }
-                        Properties overrides = data.getStyleOverrides();
-                        if (overrides != null)
-                            _styleOverrides.putAll(overrides);
-                    } catch (IOException ioe) {
-                        _log.warn("Unable to load the blog info data from " + uri, ioe);
-                    }
-                }
-            }
-        }
-        _loaded = true;
-        _updated = false;
-    }
-    
-    public boolean publishChanges() {
-        FileInputStream logo = null;
-        try {
-            if (_logo != null) {
-                logo = new FileInputStream(_logo);
-                _log.debug("Logo file is: " + _logo.length() + "bytes @ " + _logo.getAbsolutePath());
-            }
-            InputStream styleStream = createStyleStream();
-            InputStream groupStream = createGroupStream();
-            
-            String tags = BlogInfoData.TAG;
-            String subject = "n/a";
-            String headers = "";
-            String sml = "";
-            List filenames = new ArrayList();
-            List filestreams = new ArrayList();
-            List filetypes = new ArrayList();
-            if (logo != null) {
-                filenames.add(BlogInfoData.ATTACHMENT_LOGO);
-                filestreams.add(logo);
-                filetypes.add("image/png");
-            }
-            filenames.add(BlogInfoData.ATTACHMENT_STYLE_OVERRIDE);
-            filestreams.add(styleStream);
-            filetypes.add("text/plain");
-            filenames.add(BlogInfoData.ATTACHMENT_REFERENCE_GROUPS);
-            filestreams.add(groupStream);
-            filetypes.add("text/plain");
-            
-            BlogURI uri = BlogManager.instance().createBlogEntry(_user, subject, tags, headers, sml, 
-                                                                 filenames, filestreams, filetypes);
-            if (uri != null) {
-                Archive archive = BlogManager.instance().getArchive();
-                BlogInfo info = archive.getBlogInfo(_user.getBlog());
-                if (info != null) {
-                    String props[] = info.getProperties();
-                    Properties opts = new Properties();
-                    for (int i = 0; i < props.length; i++) {
-                        if (!props[i].equals(BlogInfo.SUMMARY_ENTRY_ID))
-                            opts.setProperty(props[i], info.getProperty(props[i]));
-                    }
-                    opts.setProperty(BlogInfo.SUMMARY_ENTRY_ID, uri.toString());
-                    boolean updated = BlogManager.instance().updateMetadata(_user, _user.getBlog(), opts);
-                    if (updated) {
-                        // ok great, published locally, though should we push it to others?
-                        _log.info("Blog summary updated for " + _user + " in " + uri.toString());
-                        setUser(_user);
-                        _log.debug("Updated? " + _updated);
-                        return true;
-                    }
-                } else {
-                    _log.error("Info is not known for " + _user.getBlog().toBase64());
-                    return false;
-                }
-            } else {
-                _log.error("Error creating the summary entry");
-                return false;
-            }
-        } catch (IOException ioe) {
-            _log.error("Error publishing", ioe);
-        } finally {
-            if (logo != null) try { logo.close(); } catch (IOException ioe) {}
-            // the other streams are in-memory, drop with the scope
-            if (_logo != null) _logo.delete();
-        }
-        return false;
-    }
-    private InputStream createStyleStream() throws IOException {
-        StringBuffer buf = new StringBuffer(1024);
-        if (_styleOverrides != null) {
-            for (Iterator iter = _styleOverrides.keySet().iterator(); iter.hasNext(); ) {
-                String key = (String)iter.next();
-                String val = _styleOverrides.getProperty(key);
-                buf.append(key).append('=').append(val).append('\n');
-            }
-        }
-        return new ByteArrayInputStream(DataHelper.getUTF8(buf));
-    }
-    private InputStream createGroupStream() throws IOException {
-        StringBuffer buf = new StringBuffer(1024);
-        for (int i = 0; i < _groups.size(); i++) {
-            List group = (List)_groups.get(i);
-            for (int j = 0; j < group.size(); j++) {
-                PetName pn = (PetName)group.get(j);
-                buf.append(pn.toString()).append('\n');
-            }
-        }
-        return new ByteArrayInputStream(DataHelper.getUTF8(buf));
-    }
-    
-    protected void finalize() {
-        if (_logo != null) _logo.delete();
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/BlogConfigServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/BlogConfigServlet.java
deleted file mode 100644
index c2cfc5d14310c474bd8d06163899155f0a7a4492..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/BlogConfigServlet.java
+++ /dev/null
@@ -1,451 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
-
-import javax.servlet.http.HttpServletRequest;
-
-import net.i2p.client.naming.PetName;
-import net.i2p.client.naming.PetNameDB;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.BlogInfoData;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.ThreadIndex;
-import net.i2p.syndie.sml.HTMLRenderer;
-
-/**
- * Display our blog config, and let us edit it through several screens
- *
- */
-public class BlogConfigServlet extends BaseServlet {
-    private static final String ATTR_CONFIG_BEAN = "__blogConfigBean";
-    public static final String PARAM_CONFIG_SCREEN = "screen";
-    public static final String SCREEN_REFERENCES = "references";
-    public static final String SCREEN_IMAGES = "images";
-    
-    public static BlogConfigBean getConfigBean(HttpServletRequest req, User user) {
-        BlogConfigBean bean = (BlogConfigBean)req.getSession().getAttribute(ATTR_CONFIG_BEAN);
-        if (bean == null) {
-            bean = new BlogConfigBean();
-            bean.setUser(user);
-            req.getSession().setAttribute(ATTR_CONFIG_BEAN, bean);
-        }
-        return bean;
-    }
-    public static BlogConfigBean getConfigBean(HttpServletRequest req) {
-        return (BlogConfigBean)req.getSession().getAttribute(ATTR_CONFIG_BEAN);
-    }
-    
-    protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, 
-                                        int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
-        if ( (user == null) || (!user.getAuthenticated() && !BlogManager.instance().isSingleUser())) {
-            out.write("You must be logged in to edit your profile");
-            return;
-        }
-        
-        BlogConfigBean bean = getConfigBean(req, user);
-        
-        String screen = req.getParameter(PARAM_CONFIG_SCREEN);
-        if (screen == null)
-            screen = SCREEN_REFERENCES;
-        out.write("<tr><td colspan=\"3\">\n");
-        showConfigNav(req, out);
-        
-        if (isAuthed(req)) {
-            StringBuffer buf = handleOtherAuthedActions(user, req, bean);
-            if (buf != null) out.write(buf.toString());
-        } else {
-            String contentType = req.getContentType();
-            if (!empty(contentType) && (contentType.indexOf("boundary=") != -1)) {
-                StringBuffer buf = handlePost(user, req, bean);
-                if (buf != null) out.write(buf.toString());
-            }
-        }
-        if (bean.isUpdated())
-            showCommitForm(req, out);
-        
-        if (SCREEN_REFERENCES.equals(screen)) {
-            displayReferencesScreen(req, out, user, bean);
-        } else if (SCREEN_IMAGES.equals(screen)) {
-            displayImagesScreen(req, out, user, bean);
-        } else {
-            displayUnknownScreen(out, screen);
-        }
-        out.write("</td></tr>\n");
-    }
-    private StringBuffer handlePost(User user, HttpServletRequest rawRequest, BlogConfigBean bean) throws IOException {
-        StringBuffer rv = new StringBuffer(64);
-        MultiPartRequest req = new MultiPartRequest(rawRequest);
-        if (authAction(req.getString(PARAM_AUTH_ACTION))) {
-            // read in the logo if specified
-            String filename = req.getFilename("newLogo");
-            if ( (filename != null) && (filename.trim().length() > 0) ) {
-                Hashtable params = req.getParams("newLogo");
-                String type = "image/png";
-                for (Iterator iter = params.keySet().iterator(); iter.hasNext(); ) {
-                  String cur = (String)iter.next();
-                  if ("content-type".equalsIgnoreCase(cur)) {
-                    type = (String)params.get(cur);
-                    break;
-                  }
-                }
-                InputStream logoSrc = req.getInputStream("newLogo");
-                
-                File tmpLogo = File.createTempFile("blogLogo", ".png", BlogManager.instance().getTempDir());
-                FileOutputStream out = null;
-                try {
-                    out = new FileOutputStream(tmpLogo);
-                    byte buf[] = new byte[4096];
-                    int read = 0;
-                    while ( (read = logoSrc.read(buf)) != -1)
-                        out.write(buf, 0, read);
-                } finally {
-                    if (out != null) try { out.close(); } catch (IOException ioe) {}
-                }
-
-                long len = tmpLogo.length();
-                if (len > BlogInfoData.MAX_LOGO_SIZE) {
-                    tmpLogo.delete();
-                    rv.append("Proposed logo is too large (" + len + ", max of " + BlogInfoData.MAX_LOGO_SIZE + ")<br />\n");
-                } else {
-                    bean.setLogo(tmpLogo);
-                    rv.append("Logo updated<br />");
-                }
-            } else {
-                // logo not specified
-            }
-        } else {
-            // noop
-        }
-        return rv;
-    }
-    
-    private void showCommitForm(HttpServletRequest req, PrintWriter out) throws IOException {
-        out.write("<form action=\"" + req.getRequestURI() + "\" method=\"GET\">\n");
-        writeAuthActionFields(out);
-        out.write("<i>Note: Uncommitted changes outstanding</a> <input type=\"submit\" name=\"action\" " +
-                  "value=\"Publish blog configuration\" />\n</form>\n");
-    }
-    
-    private void showConfigNav(HttpServletRequest req, PrintWriter out) throws IOException {
-        out.write("<span class=\"syndieBlogConfigNav\"><a href=\"" + getScreenURL(req, SCREEN_REFERENCES, false)
-                  + "\" title=\"Configure the blog's references\">References</a> "
-                  + "<a href=\"" + getScreenURL(req, SCREEN_IMAGES, false)
-                  + "\" title=\"Configure the images used on the blog\">Images</a></span><hr />\n");
-    }
-    
-    private String getScreenURL(HttpServletRequest req, String screen, boolean wantAuth) {
-        StringBuffer buf = new StringBuffer(128);
-        buf.append(req.getRequestURI()).append("?").append(PARAM_CONFIG_SCREEN).append("=");
-        buf.append(screen).append("&amp;");
-        if (wantAuth)
-            buf.append(PARAM_AUTH_ACTION).append('=').append(_authNonce).append("&amp;");
-        return buf.toString();
-    }
-    
-    private void displayUnknownScreen(PrintWriter out, String screen) throws IOException {
-        out.write("<br /><hr />The screen " + HTMLRenderer.sanitizeString(screen) + " has not yet been implemented");
-    }
-    private void displayReferencesScreen(HttpServletRequest req, PrintWriter out, User user, BlogConfigBean bean) throws IOException {
-        out.write("<form action=\"" + getScreenURL(req, SCREEN_REFERENCES, false) + "\" method=\"POST\">\n");
-        writeAuthActionFields(out);
-        out.write("<ol class=\"syndieReferenceGroupList\">\n");
-        boolean defaultFound = false;
-        for (int i = 0; i < bean.getGroupCount(); i++) {
-            List group = bean.getGroup(i);
-            String groupName = null;
-            PetName pn = (PetName)group.get(0);
-            if (pn.getGroupCount() <= 0) {
-                groupName = ViewBlogServlet.DEFAULT_GROUP_NAME;
-                defaultFound = true;
-            } else {
-                groupName = pn.getGroup(0);
-            }
-            out.write("<li><b>Group:</b> " + HTMLRenderer.sanitizeString(groupName) + "\n");
-            if (i > 0)
-                out.write(" <a href=\"" + getScreenURL(req, SCREEN_REFERENCES, true) + "moveFrom=" + i + 
-                          "&amp;moveTo=" + (i-1) + "\" title=\"Move higher\">^</a>");
-            if (i + 1 < bean.getGroupCount())
-                out.write(" <a href=\"" + getScreenURL(req, SCREEN_REFERENCES, true) + "moveFrom=" + i + 
-                          "&amp;moveTo=" + (i+1) + "\" title=\"Move lower\">v</a>");
-            out.write(" <a href=\"" + getScreenURL(req, SCREEN_REFERENCES, true) + "delete=" + i + "\" title=\"Delete\">X</a>");
-            
-            out.write("<ol class=\"syndieReferenceGroupElementList\">\n");
-            for (int j = 0; j < group.size(); j++) {
-                out.write("<li>" + ViewBlogServlet.renderLink(user.getBlog(), (PetName)group.get(j)));
-                if (j > 0)
-                    out.write(" <a href=\"" + getScreenURL(req, SCREEN_REFERENCES, true) + "moveRefFrom=" + i + "." + j + 
-                              "&amp;moveRefTo=" + i + "." + (j-1) + "\" title=\"Move higher\">^</a>");
-                if (j + 1 < group.size())
-                    out.write(" <a href=\"" + getScreenURL(req, SCREEN_REFERENCES, true) + "moveRefFrom=" + i + "." + j + 
-                              "&amp;moveRefTo=" + i + "." + (j+1) + "\" title=\"Move lower\">v</a>");
-                out.write(" <a href=\"" + getScreenURL(req, SCREEN_REFERENCES, true) + "delete=" + i + "." + j 
-                          + "\" title=\"Delete\">X</a>");
-                out.write("</li>\n");
-            }
-            out.write("</ol><!-- end of the syndieReferenceGroupElementList -->\n");
-            out.write("</li>\n");
-        }
-        out.write("</ol><!-- end of the syndieReferenceGroupList -->\n");
-        
-        
-        out.write("Add a new element: <br />Group: <select name=\"new.group\"><option value=\"\">Select a group...</option>");
-        for (int i = 0; i < bean.getGroupCount(); i++) {
-            List group = bean.getGroup(i);
-            String groupName = null;
-            PetName pn = (PetName)group.get(0);
-            if (pn.getGroupCount() <= 0)
-                groupName = ViewBlogServlet.DEFAULT_GROUP_NAME;
-            else
-                groupName = pn.getGroup(0);
-            if (groupName != null)
-                out.write("<option value=\"" + HTMLRenderer.sanitizeTagParam(groupName) + "\">" + 
-                          HTMLRenderer.sanitizeString(groupName) + "</option>\n");
-        }
-        if (!defaultFound)
-            out.write("<option value=\"" + ViewBlogServlet.DEFAULT_GROUP_NAME + "\">" 
-                      + ViewBlogServlet.DEFAULT_GROUP_NAME + "</option>\n");
-        out.write("</select> or <input type=\"text\" size=\"12\" name=\"new.groupOther\" /><br />" +
-                  "Type: <select name=\"new.type\"><option value=\"blog\">Syndie blog</option>\n" +
-                  "<option value=\"blogpost\">Post within a syndie blog</option>\n" +
-                  "<option value=\"blogpostattachment\">Attachment within a syndie blog</option>\n" +
-                  "<option value=\"eepsite\">Eepsite</option>\n" +
-                  "<option value=\"website\">Website</option>\n</select><br />\n" +
-                  "Name: <input type=\"text\" size=\"20\" name=\"new.name\" /><br /> " +
-                  "Location: <input type=\"text\" name=\"new.location\" size=\"40\" />\n" +
-                  "<ul><li>Blogs should be specified as <code>$base64Key</code></li>\n" +
-                  "<li>Blog posts should be specified as <code>$base64Key/$postEntryId</code></li>\n" +
-                  "<li>Blog post attachments should be specified as <code>$base64Key/$postEntryId/$attachmentNum</code></li>\n" +
-                  "</ul><hr />\n");
-        
-        out.write("<input type=\"submit\" name=\"action\" value=\"Save changes\">\n");
-        out.write("</form>\n");
-    }
-    
-    private void writePetnameDropdown(PrintWriter out, PetNameDB db) throws IOException {
-        Set names = db.getNames();
-        TreeSet ordered = new TreeSet(names);
-        for (Iterator iter = ordered.iterator(); iter.hasNext(); ) {
-            String name = (String)iter.next();
-            PetName pn = db.getByName(name);
-            String proto = pn.getProtocol();
-            if ("syndietag".equals(proto))
-                continue;
-            out.write("<option value=\"" + HTMLRenderer.sanitizeTagParam(pn.getName()) + "\">");
-            if ("syndieblog".equals(proto))
-                out.write("Blog: ");
-            else if ("syndiearchive".equals(proto))
-                out.write("Archive: ");
-            else if ("eep".equals(proto))
-                out.write("Eepsite: ");
-            else
-                out.write(HTMLRenderer.sanitizeString(proto) + ": ");
-            out.write(HTMLRenderer.sanitizeString(pn.getName()) + "</option>\n");
-        }
-    }
-    
-    private void displayImagesScreen(HttpServletRequest req, PrintWriter out, User user, BlogConfigBean bean) throws IOException {
-        out.write("<form action=\"" + getScreenURL(req, SCREEN_IMAGES, false) + "\" method=\"POST\" enctype=\"multipart/form-data\">\n");
-        writeAuthActionFields(out);
-
-        File logo = bean.getLogo();
-        if (logo != null)
-            out.write("Blog logo: <img src=\"" + ViewBlogServlet.getLogoURL(user.getBlog()) + "\" alt=\"Your blog's logo\" /><br />\n");
-        out.write("New logo: <input type=\"file\" name=\"newLogo\" title=\"PNG or JPG format logo\" /><br />\n");
-        out.write("<input type=\"submit\" name=\"action\" value=\"Save changes\">\n");
-        out.write("</form>\n");        
-    }
-    
-    protected StringBuffer handleOtherAuthedActions(User user, HttpServletRequest req, BlogConfigBean bean) {
-        StringBuffer buf = new StringBuffer();
-        req.setAttribute(getClass().getName() + ".output", buf);
-        String action = req.getParameter("action");
-        if ("Publish blog configuration".equals(action)) {
-            if (bean.publishChanges()) {
-                buf.append("Changes published<br />\n");
-            } else {
-                buf.append("Changes could not be published (please check the log)<br />\n");
-            }
-        } else {
-            if ("Save changes".equals(action)) {
-                String newGroup = req.getParameter("new.group");
-                if ( (newGroup == null) || (newGroup.trim().length() <= 0) )
-                    newGroup = req.getParameter("new.groupOther");
-                if ( (newGroup != null) && (newGroup.trim().length() > 0) ) {
-                    addElement(req, user, newGroup, buf, bean);
-                } else {
-                }
-            } else {
-            }
-
-            handleDelete(req, user, bean, buf);
-            handleReorderGroup(req, user, bean, buf);
-            handleReorderRef(req, user, bean, buf);
-        }
-        return buf;
-    }
-    
-    private void addElement(HttpServletRequest req, User user, String newGroup, StringBuffer actionOutputHTML, BlogConfigBean bean) {
-        String type = req.getParameter("new.type");
-        String loc = req.getParameter("new.location");
-        String name = req.getParameter("new.name");
-        
-        if (empty(type) || empty(loc) || empty(name)) return;
-        
-        PetName pn = null;
-        if ("blog".equals(type))
-            pn = new PetName(name, "syndie", "syndieblog", loc);
-        else if ("blogpost".equals(type))
-            pn = new PetName(name, "syndie", "syndieblogpost", loc);
-        else if ("blogpostattachment".equals(type))
-            pn = new PetName(name, "syndie", "syndieblogattachment", loc);
-        else if ("eepsite".equals(type))
-            pn = new PetName(name, "i2p", "http", loc);
-        else if ("website".equals(type))
-            pn = new PetName(name, "web", "http", loc);
-        else {
-            // unknown type
-        }
-        
-        if (pn != null) {
-            if (!ViewBlogServlet.DEFAULT_GROUP_NAME.equals(newGroup))
-                pn.addGroup(newGroup);
-            bean.add(pn);
-            actionOutputHTML.append("Reference '").append(HTMLRenderer.sanitizeString(name));
-            actionOutputHTML.append("' for ").append(HTMLRenderer.sanitizeString(loc)).append(" added to ");
-            actionOutputHTML.append(HTMLRenderer.sanitizeString(newGroup)).append("<br />\n");
-        }
-    }
-
-    private void handleDelete(HttpServletRequest req, User user, BlogConfigBean bean, StringBuffer actionOutputHTML) {
-        // control parameters: 
-        //   delete=$i    removes group # $i
-        //   delete=$i.$j removes element $j in group $i
-        String del = req.getParameter("delete");
-        if (empty(del)) return;
-        int split = del.indexOf('.');
-        int group = -1;
-        int elem = -1;
-        if (split <= 0) {
-            try { group = Integer.parseInt(del); } catch (NumberFormatException nfe) {}
-        } else {
-            try { 
-                group = Integer.parseInt(del.substring(0, split)); 
-                elem = Integer.parseInt(del.substring(split+1)); 
-            } catch (NumberFormatException nfe) {
-                group = -1;
-                elem = -1;
-            }
-        }
-        if ( (elem >= 0) && (group >= 0) ) {
-            List l = bean.getGroup(group);
-            if (elem < l.size()) {
-                PetName pn = (PetName)l.get(elem);
-                bean.remove(pn);
-                actionOutputHTML.append("Reference '").append(HTMLRenderer.sanitizeString(pn.getName()));
-                actionOutputHTML.append("' for ").append(HTMLRenderer.sanitizeString(pn.getLocation()));
-                actionOutputHTML.append(" removed<br />\n");
-            }
-        } else if ( (elem == -1) && (group >= 0) ) {
-            List l = bean.getGroup(group);
-            for (int i = 0; i < l.size(); i++) {
-                PetName pn = (PetName)l.get(i);
-                bean.remove(pn);
-            }
-            actionOutputHTML.append("All references in the selected group were removed<br />\n");
-        } else {
-            // noop
-        }
-    }
-
-    private void handleReorderGroup(HttpServletRequest req, User user, BlogConfigBean bean, StringBuffer actionOutputHTML) {
-        // control parameters: 
-        //   moveFrom=$i & moveTo=$j moves group $i to position $j
-        int from = -1;
-        int to = -1;
-        try { 
-            String str = req.getParameter("moveFrom");
-            if (str != null)
-                from = Integer.parseInt(str);
-            str = req.getParameter("moveTo");
-            if (str != null)
-                to = Integer.parseInt(str);
-            
-            if ( (from >= 0) && (to >= 0) ) {
-                List src = bean.getGroup(from);
-                List dest = bean.getGroup(to);
-                List orig = new ArrayList(dest);
-                dest.clear();
-                dest.addAll(src);
-                src.clear();
-                src.addAll(orig);
-                bean.groupsUpdated();
-                actionOutputHTML.append("Reference group moved<br />\n");
-            }
-        } catch (NumberFormatException nfe) {
-            // ignore
-        }
-    }
-
-    private void handleReorderRef(HttpServletRequest req, User user, BlogConfigBean bean, StringBuffer actionOutputHTML) {
-        // control parameters: 
-        //   moveRefFrom=$i.$j & moveRefTo=$k.$l moves element $j in group $i to position $l in group l
-        //   (i == k)
-        int from = -1;
-        int fromElem = -1;
-        int to = -1; // ignored
-        int toElem = -1;
-        try { 
-            String str = req.getParameter("moveRefFrom");
-            if (str != null) {
-                int split = str.indexOf('.');
-                if (split > 0) {
-                    try { 
-                        from = Integer.parseInt(str.substring(0, split)); 
-                        fromElem = Integer.parseInt(str.substring(split+1)); 
-                    } catch (NumberFormatException nfe) {
-                        from = -1;
-                        fromElem = -1;
-                    }
-                }
-            }
-            str = req.getParameter("moveRefTo");
-            if (str != null) {
-                int split = str.indexOf('.');
-                if (split > 0) {
-                    try { 
-                        to = Integer.parseInt(str.substring(0, split)); 
-                        toElem = Integer.parseInt(str.substring(split+1)); 
-                    } catch (NumberFormatException nfe) {
-                        to = -1;
-                        toElem = -1;
-                    }
-                }
-            }
-            
-            if ( (from >= 0) && (fromElem >= 0) && (toElem >= 0) ) {
-                List src = bean.getGroup(from);
-                PetName pn = (PetName)src.remove(fromElem);
-                src.add(toElem, pn);
-                bean.groupsUpdated();
-                actionOutputHTML.append("Reference element moved<br />\n");
-            }
-        } catch (NumberFormatException nfe) {
-            // ignore
-        }
-    }
-    
-    
-    protected String getTitle() { return "Syndie :: Configure blog"; }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ExportServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ExportServlet.java
deleted file mode 100644
index 5bcd2f2c69a13085c032e12692b41d202c569711..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/ExportServlet.java
+++ /dev/null
@@ -1,210 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import net.i2p.data.Base64;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.data.BlogURI;
-
-/**
- * Dump out a whole series of blog metadata and entries as a zip stream.  All metadata
- * is written before any entries, so it can be processed in order safely.
- *
- * HTTP parameters: 
- *  = meta (multiple values): base64 hash of the blog for which metadata is requested
- *  = entry (multiple values): blog URI of an entry being requested
- */
-public class ExportServlet extends HttpServlet {
-    
-    public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        export(req, resp);
-    }
-    
-    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        export(req, resp);
-    }
-    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        export(req, resp);
-    }
-    public void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        export(req, resp);
-    }
-    
-    public static void export(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        try {
-            doExport(req, resp);
-        } catch (ServletException se) {
-            se.printStackTrace();
-            throw se;
-        } catch (IOException ioe) {
-            ioe.printStackTrace();
-            throw ioe;
-        }
-    }
-    private static void doExport(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        String meta[] = null;
-        String entries[] = null;
-        String type = req.getHeader("Content-Type");
-        if ( (type == null) || (type.indexOf("boundary") == -1) ) {
-            // it has to be POSTed with the request, name=value pairs.  the export servlet doesn't allow any
-            // free form fields, so no worry about newlines, so lets parse 'er up
-            List metaList = new ArrayList();
-            List entryList = new ArrayList();
-            StringBuffer key = new StringBuffer();
-            StringBuffer val = null;
-            String lenStr = req.getHeader("Content-length");
-            int len = -1;
-            if (lenStr != null)
-                try { len = Integer.valueOf(lenStr).intValue(); } catch (NumberFormatException nfe) {}
-
-            int read = 0;
-            int c = 0;
-            InputStream in = req.getInputStream();
-            while ( (len == -1) || (read < len) ){
-                c = in.read();
-                if ( (c == '=') && (val == null) ) {
-                    val = new StringBuffer(128);
-                } else if ( (c == -1) || (c == '&') ) {
-                    String k = (key == null ? "" : key.toString());
-                    String v = (val == null ? "" : val.toString());
-                    if ("meta".equals(k))
-                        metaList.add(v.trim());
-                    else if ("entry".equals(k))
-                        entryList.add(v.trim());
-                    key.setLength(0);
-                    val = null;
-                    // no newlines in the export servlet
-                    if (c == -1)
-                        break;
-                } else {
-                    if (val == null)
-                        key.append((char)c);
-                    else
-                        val.append((char)c);
-                }
-                read++;
-            }
-            if (metaList != null) {
-                meta = new String[metaList.size()];
-                for (int i = 0; i < metaList.size(); i++)
-                    meta[i] = (String)metaList.get(i);
-            }
-            if (entryList != null) {
-                entries = new String[entryList.size()];
-                for (int i = 0; i < entryList.size(); i++)
-                    entries[i] = (String)entryList.get(i);
-            }
-        } else {
-            meta = req.getParameterValues("meta");
-            entries = req.getParameterValues("entry");
-        }
-        resp.setContentType("application/x-syndie-zip");
-        resp.setStatus(200);
-        OutputStream out = resp.getOutputStream();
-        
-        if (false) {
-            StringBuffer bbuf = new StringBuffer(1024);
-            bbuf.append("meta: ");
-            if (meta != null)
-                for (int i = 0; i < meta.length; i++)
-                    bbuf.append(meta[i]).append(", ");
-            bbuf.append("entries: ");
-            if (entries != null)
-                for (int i = 0; i < entries.length; i++)
-                    bbuf.append(entries[i]).append(", ");
-            System.out.println(bbuf.toString());
-        }
-        
-        ZipOutputStream zo = null;
-        if ( (meta != null) && (entries != null) && (meta.length + entries.length > 0) )
-            zo = new ZipOutputStream(out);
-        
-        List metaFiles = getMetaFiles(meta);
-        
-        ZipEntry ze = null;
-        byte buf[] = new byte[1024];
-        int read = -1;
-        for (int i = 0; metaFiles != null && i < metaFiles.size(); i++) {
-            ze = new ZipEntry("meta" + i);
-            ze.setTime(0);
-            zo.putNextEntry(ze);
-            FileInputStream in = null;
-	    try {
-                in = new FileInputStream((File)metaFiles.get(i));
-                while ( (read = in.read(buf)) != -1)
-                    zo.write(buf, 0, read);
-                zo.closeEntry();
-            } finally {
-                if (in != null) try { in.close(); } catch (IOException ioe) {}
-            }
-        }
-        
-        List entryFiles = getEntryFiles(entries);
-        for (int i = 0; entryFiles != null && i < entryFiles.size(); i++) {
-            ze = new ZipEntry("entry" + i);
-            ze.setTime(0);
-            zo.putNextEntry(ze);
-            FileInputStream in = null;
-	    try {
-                in = new FileInputStream((File)entryFiles.get(i));
-                while ( (read = in.read(buf)) != -1) 
-                    zo.write(buf, 0, read);
-                zo.closeEntry();
-	    } finally {
-                if (in != null) try { in.close(); } catch (IOException ioe) {}
-            }
-        }
-        
-        if (zo != null) {
-            zo.finish();
-            zo.close();
-        }
-    }
-    
-    private static List getMetaFiles(String blogHashes[]) {
-        if ( (blogHashes == null) || (blogHashes.length <= 0) ) return null;
-        File dir = BlogManager.instance().getArchive().getArchiveDir();
-        List rv = new ArrayList(blogHashes.length);
-        for (int i = 0; i < blogHashes.length; i++) {
-            byte hv[] = Base64.decode(blogHashes[i]);
-            if ( (hv == null) || (hv.length != Hash.HASH_LENGTH) )
-                continue;
-            File blogDir = new File(dir, blogHashes[i]);
-            File metaFile = new File(blogDir, Archive.METADATA_FILE);
-            if (metaFile.exists())
-                rv.add(metaFile);
-        }
-        return rv;
-    }
-    
-    private static List getEntryFiles(String blogURIs[]) {
-        if ( (blogURIs == null) || (blogURIs.length <= 0) ) return null;
-        File dir = BlogManager.instance().getArchive().getArchiveDir();
-        List rv = new ArrayList(blogURIs.length);
-        for (int i = 0; i < blogURIs.length; i++) {
-            BlogURI uri = new BlogURI(blogURIs[i]);
-            if (uri.getEntryId() < 0)
-                continue;
-            File blogDir = new File(dir, uri.getKeyHash().toBase64());
-            File entryFile = new File(blogDir, uri.getEntryId() + ".snd");
-            if (entryFile.exists())
-                rv.add(entryFile);
-        }
-        return rv;
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ExternalLinkServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ExternalLinkServlet.java
deleted file mode 100644
index f89e58079d506def0e20d7e901df2840c7bf7c6b..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/ExternalLinkServlet.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-
-import javax.servlet.http.HttpServletRequest;
-
-import net.i2p.data.Base64;
-import net.i2p.data.DataHelper;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.ThreadIndex;
-import net.i2p.syndie.sml.HTMLRenderer;
-
-/**
- * Confirm page before hitting a remote site
- *
- */
-public class ExternalLinkServlet extends BaseServlet { 
-    protected String getTitle() { return "Syndie :: External link"; }
-    
-    protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, 
-                                        int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
-        String b64Schema = req.getParameter("schema");
-        String b64Location = req.getParameter("location");
-        if ( (b64Schema == null) || (b64Schema.trim().length() <= 0) ||
-             (b64Location == null) || (b64Location.trim().length() <= 0) ) {
-            out.write("<tr><td colspan=\"3\">No location specified</td></tr>\n");
-        } else {
-            byte loc[] = Base64.decode(b64Location);
-            if ( (loc == null) || (loc.length <= 0) ) {
-                out.write("<tr><td colspan=\"3\">Invalid location specified</td></tr>\n");
-            } else {
-                String location = DataHelper.getUTF8(loc);
-                out.write("<tr><td colspan=\"3\">Are you sure you want to go to <a href=\"");
-                out.write(HTMLRenderer.sanitizeTagParam(location));
-                out.write("\" title=\"Link to an external site\">");
-                out.write(HTMLRenderer.sanitizeString(location));
-                out.write("</a></td></tr>\n");
-            }
-        }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ImportFeedServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ImportFeedServlet.java
deleted file mode 100644
index 9b603bbbf79a5f2b28864954d2a876149626afe5..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/ImportFeedServlet.java
+++ /dev/null
@@ -1,149 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.servlet.http.HttpServletRequest;
-
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.ThreadIndex;
-
-/**
- * Schedule the import of atom/rss feeds
- */
-public class ImportFeedServlet extends BaseServlet { 
-    protected String getTitle() { return "Syndie :: Import feed"; }
-    
-    protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, 
-                                        int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
- 
-        if (!BlogManager.instance().authorizeRemote(user)) {
-            out.write("<tr><td colspan=\"3\"><span class=\"b_rssMsgErr\">You are not authorized for remote access.</span></td></tr>\n");
-            return;
-        } else {
-            out.write("<tr><td colspan=\"3\">");
-          
-            String url=req.getParameter("url");
-            if (url != null) 
-                url = url.trim();
-            String blog=req.getParameter("blog");
-            if (blog != null)
-                blog=blog.trim();
-            String tagPrefix = req.getParameter("tagprefix");
-            if (tagPrefix != null)
-                tagPrefix=tagPrefix.trim();
-            String action = req.getParameter("action");
-            if ( (action != null) && ("Add".equals(action)) ) {
-                if(url==null || blog==null || tagPrefix==null) {
-                    out.write("<span class=\"b_rssImportMsgErr\">Please fill in all fields</span><br />\n");
-                } else {
-                    boolean ret = BlogManager.instance().addRssFeed(url, blog, tagPrefix);
-                    if (!ret) {
-                        out.write("<span class=\"b_rssImportMsgErr\">addRssFeed failure.</span>");
-                    } else {
-                        out.write("<span class=\"b_rssImportMsgOk\">RSS feed added.</span>");
-                    }
-                }
-            } else if ( (action != null) && ("Change".equals(action)) ) {
-                String lastUrl=req.getParameter("lasturl");
-                String lastBlog=req.getParameter("lastblog");
-                String lastTagPrefix=req.getParameter("lasttagprefix");
-              
-                if (url == null || blog == null || tagPrefix == null || 
-                    lastUrl == null || lastBlog == null || lastTagPrefix == null) {
-                    out.write("<span class=\"b_rssImportMsgErr\">error, some fields were empty.</span><br />");
-                } else {
-                    boolean ret = BlogManager.instance().deleteRssFeed(lastUrl,lastBlog,lastTagPrefix);
-                    if (!ret) {
-                        out.write("<span class=\"b_rssImportMsgErr\">Could not delete while attempting to change.</span>");
-                    } else {
-                        ret = BlogManager.instance().addRssFeed(url,blog,tagPrefix);
-                        if (!ret) {
-                            out.write("<span class=\"b_rssImportMsgErr\">Could not add while attempting to change.</span>");
-                        } else {
-                            out.write("<span class=\"b_rssImportMsgOk\">Ok, changed successfully.</span>");
-                        }
-                    }
-                }
-            } else if ( (action != null) && ("Delete".equals(action)) ) {
-                if (url == null || blog == null || tagPrefix == null) {
-                    out.write("<span class=\"b_rssImportMsgErr\">error, some fields were empty.</span><br />");
-                } else {
-                    boolean ret = BlogManager.instance().deleteRssFeed(url,blog,tagPrefix);
-                    if (!ret) {
-                        out.write("<span class=\"b_rssImportMsgErr\">error, could not delete.</span>");
-                    } else {
-                        out.write("<span class=\"b_rssImportMsgOk\">ok, deleted successfully.</span>");
-                    }
-                }
-            }
-          
-            String blogStr = user.getBlogStr();
-            if (blogStr == null)
-                blogStr="";
-    
-            out.write("<p>Here you can add RSS feeds that will be periodically polled and added to your syndie. </p>");
-            out.write("<form action=\"" + req.getRequestURI() + "\" method=\"POST\">");
-            writeAuthActionFields(out);
-            out.write("RSS URL. (e.g. http://tracker.postman.i2p/rss.php)<br />\n");
-            out.write("<em><span class=\"b_rssImportField\">url:</span></em> <input class=\"b_rssImportField\" type=\"text\" size=\"50\" name=\"url\" /><br />\n");
-            out.write("Blog hash to which the RSS entries will get posted, defaults to the one you're logged in to.<br />\n");
-            out.write("<em><span class=\"b_rssImportField\">blog:</span></em> <input class=\"b_rssImportField\" type=\"text\" value=\"");
-            out.write(blogStr);
-            out.write("\" size=\"20\" name=\"blog\" /><br />\n");
-            out.write("This will be prepended to any tags that the RSS feed contains. (e.g. feed.tracker)<br />\n");
-            out.write("<em><span class=\"b_rssImportField\">tagprefix:</span></em>\n");
-            out.write("<input class=\"b_rssImportField\" type=\"text\" value=\"feed\" size=\"20\" name=\"tagprefix\" /><br />\n");
-            out.write("<input class=\"b_rssImportSubmit\" type=\"submit\" name=\"action\" value=\"Add\" />\n");
-            out.write("<input class=\"b_rssImportCancel\" type=\"reset\" value=\"Cancel\" />\n");
-            out.write("</form>\n");
-
-            List feedList = BlogManager.instance().getRssFeeds();
-            if (feedList.size()>0) {
-                out.write("<hr /><h3>Subscriptions:</h3><br />\n");
-                out.write("<table border=\"0\" width=\"100%\" class=\"b_rss\">\n");
-                out.write("<tr class=\"b_rssHeader\">\n");
-                out.write("<td class=\"b_rssHeader\"><em class=\"b_rssHeader\">Url</em></td>\n");
-                out.write("<td class=\"b_rssHeader\"><em class=\"b_rssHeader\">Blog</em></td>\n");
-                out.write("<td class=\"b_rssHeader\"><em class=\"b_rssHeader\">TagPrefix</em></td>\n");
-                out.write("<td class=\"b_rssHeader\">&nbsp;</td></tr>\n");
-
-                Iterator iter = feedList.iterator();
-                while (iter.hasNext()) {
-                    String fields[] = (String[])iter.next();
-                    url = fields[0];
-                    blog = fields[1];
-                    tagPrefix = fields[2];
-                    StringBuffer buf = new StringBuffer(128);
-            
-                    buf.append("<tr class=\"b_rssDetail\"><form action=\"" + req.getRequestURI() + "\" method=\"POST\">");
-                    writeAuthActionFields(out);
-                    buf.append("<input type=\"hidden\" name=\"lasturl\" value=\"").append(url).append("\" />");
-                    buf.append("<input type=\"hidden\" name=\"lastblog\" value=\"").append(blog).append("\" />");
-                    buf.append("<input type=\"hidden\" name=\"lasttagprefix\" value=\"").append(tagPrefix).append("\" />");
-
-                    buf.append("<td class=\"b_rssUrl\"><input class=\"b_rssUrl\" type=\"text\" size=\"50\" name=\"url\" value=\"").append(url).append("\" /></td>");
-                    buf.append("<td class=\"b_rssBlog\"><input class=\"b_rssBlog\" type=\"text\" size=\"20\" name=\"blog\" value=\"").append(blog).append("\" /></td>");
-                    buf.append("<td class=\"b_rssPrefix\"><input class=\"b_rssPrefix\" type=\"text\" size=\"20\" name=\"tagprefix\" value=\"").append(tagPrefix).append("\" /></td>");
-                    buf.append("<td class=\"b_rssDetail\" nowrap=\"nowrap\">");
-
-                    buf.append("<input class=\"b_rssChange\" type=\"submit\" name=\"action\" value=\"Change\" />");
-                    buf.append("<input class=\"b_rssDelete\" type=\"submit\" name=\"action\" value=\"Delete\" />");
-                  
-                    buf.append("</td></form></tr>");
-                    out.write(buf.toString());
-                    buf.setLength(0);
-                }
-              
-                out.write("</table>\n");
-            } // end iterating over feeds
-          
-            out.write("</td></tr>\n");
-        }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/MultiPartRequest.java b/apps/syndie/java/src/net/i2p/syndie/web/MultiPartRequest.java
deleted file mode 100644
index 22f73b40a09b35d95e8a8c4e89e75adf0f2c2382..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/MultiPartRequest.java
+++ /dev/null
@@ -1,422 +0,0 @@
-// see below for license info
-package net.i2p.syndie.web;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Set;
-import java.util.StringTokenizer;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.mortbay.http.HttpFields;
-import org.mortbay.util.LineInput;
-import org.mortbay.util.MultiMap;
-import org.mortbay.util.StringUtil;
-
-/* ------------------------------------------------------------ */
-/**
- * Hacked version of Jetty's MultiPartRequest handler, applying a tiny patch for
- * charset handling [1].  These changes are public domain, and will hopefully
- * be integrated into Jetty so we can drop this file altogether.  Of course,
- * until then, this file is APL2 licensed.
- *
- * Original code is up at [2]
- *
- * [1] http://article.gmane.org/gmane.comp.java.jetty.general/6031
- * [2] http://cvs.sourceforge.net/viewcvs.py/jetty/Jetty/src/org/mortbay/servlet/
- *     (rev 1.15)
- *
- */
-public class MultiPartRequest
-{
-    /* ------------------------------------------------------------ */
-    HttpServletRequest _request;
-    LineInput _in;
-    String _boundary;
-    String _encoding;
-    byte[] _byteBoundary;
-    MultiMap _partMap = new MultiMap(10);
-    int _char=-2;
-    boolean _lastPart=false;
-    
-    /* ------------------------------------------------------------ */
-    /** Constructor. 
-     * @param request The request containing a multipart/form-data
-     * request
-     * @exception IOException IOException
-     */
-    public MultiPartRequest(HttpServletRequest request)
-        throws IOException
-    {
-        _request=request;
-        String content_type = request.getHeader(HttpFields.__ContentType);
-        if (!content_type.startsWith("multipart/form-data"))
-            throw new IOException("Not multipart/form-data request");
-
-        //if(log.isDebugEnabled())log.debug("Multipart content type = "+content_type);
-        
-        _encoding = request.getCharacterEncoding();
-        if (_encoding != null)
-            _in = new LineInput(request.getInputStream(), 2048, _encoding);
-        else
-            _in = new LineInput(request.getInputStream());
-        
-        // Extract boundary string
-        _boundary="--"+
-            value(content_type.substring(content_type.indexOf("boundary=")));
-        
-        //if(log.isDebugEnabled())log.debug("Boundary="+_boundary);
-        _byteBoundary= (_boundary+"--").getBytes(StringUtil.__ISO_8859_1);
-        
-        loadAllParts();
-    }
-    
-    /* ------------------------------------------------------------ */
-    /** Get the part names.
-     * @return an array of part names
-     */
-    public String[] getPartNames()
-    {
-        Set s = _partMap.keySet();
-        return (String[]) s.toArray(new String[s.size()]);
-    }
-    
-    /* ------------------------------------------------------------ */
-    /** Check if a named part is present 
-     * @param name The part
-     * @return true if it was included 
-     */
-    public boolean contains(String name)
-    {
-        Part part = (Part)_partMap.get(name);
-        return (part!=null);
-    }
-    
-    /* ------------------------------------------------------------ */
-    /** Get the data of a part as a string.
-     * @param name The part name 
-     * @return The part data
-     */
-    public String getString(String name)
-    {
-        List part = (List)_partMap.getValues(name);
-        if (part==null)
-            return null;
-        if (_encoding != null)
-        {
-            try 
-            {
-                return new String(((Part)part.get(0))._data, _encoding);
-            }
-            catch (UnsupportedEncodingException uee)
-            {
-                //if (log.isDebugEnabled()) log.debug("Invalid character set: " + uee);
-                return null;
-            }
-        }
-        else 
-        {
-            return new String(((Part)part.get(0))._data);
-        }
-    }
-    
-    /* ------------------------------------------------------------ */
-    /** 
-     * @param name The part name 
-     * @return The parts data
-     */
-    public String[] getStrings(String name)
-    {
-        List parts = (List)_partMap.getValues(name);
-        if (parts==null)
-            return null;
-        String[] strings = new String[parts.size()];
-        if (_encoding == null) {
-            for (int i=0; i<strings.length; i++) {
-                strings[i] = new String(((Part)parts.get(i))._data);
-            }
-        } else {
-            try
-            {
-                for (int i=0; i<strings.length; i++)
-                strings[i] = new String(((Part)parts.get(i))._data, _encoding);
-            }
-            catch (UnsupportedEncodingException uee)
-            {
-                //if (log.isDebugEnabled()) log.debug("Invalid character set: " + uee);
-                return null;
-            }
-        }
-        return strings;
-    }
-    
-    /* ------------------------------------------------------------ */
-    /** Get the data of a part as a stream.
-     * @param name The part name 
-     * @return Stream providing the part data
-     */
-    public InputStream getInputStream(String name)
-    {
-        List part = (List)_partMap.getValues(name);
-        if (part==null)
-            return null;
-        return new ByteArrayInputStream(((Part)part.get(0))._data);
-    }
-
-    /* ------------------------------------------------------------ */
-    public InputStream[] getInputStreams(String name) 
-    {
-        List parts = (List)_partMap.getValues(name);
-        if (parts==null)
-            return null;
-        InputStream[] streams = new InputStream[parts.size()];
-        for (int i=0; i<streams.length; i++) {
-            streams[i] = new ByteArrayInputStream(((Part)parts.get(i))._data);
-        }
-        return streams;
-    }
-
-    /* ------------------------------------------------------------ */
-    /** Get the MIME parameters associated with a part.
-     * @param name The part name 
-     * @return Hashtable of parameters
-     */
-    public Hashtable getParams(String name)
-    {
-        List part = (List)_partMap.getValues(name);
-        if (part==null)
-            return null;
-        return ((Part)part.get(0))._headers;
-    }
-
-    /* ------------------------------------------------------------ */
-    public Hashtable[] getMultipleParams(String name) 
-    {
-        List parts = (List)_partMap.getValues(name);
-        if (parts==null)
-            return null;
-        Hashtable[] params = new Hashtable[parts.size()];
-        for (int i=0; i<params.length; i++) {
-            params[i] = ((Part)parts.get(i))._headers;
-        }
-        return params;
-    }
-
-    /* ------------------------------------------------------------ */
-    /** Get any file name associated with a part.
-     * @param name The part name 
-     * @return The filename
-     */
-    public String getFilename(String name)
-    {
-        List part = (List)_partMap.getValues(name);
-        if (part==null)
-            return null;
-        return ((Part)part.get(0))._filename;
-    }
-
-    /* ------------------------------------------------------------ */
-    public String[] getFilenames(String name) 
-    {
-        List parts = (List)_partMap.getValues(name);
-        if (parts==null)
-            return null;
-        String[] filenames = new String[parts.size()];
-        for (int i=0; i<filenames.length; i++) {
-            filenames[i] = ((Part)parts.get(i))._filename;
-        }
-        return filenames;
-    }
-
-    /* ------------------------------------------------------------ */
-    private void loadAllParts()
-        throws IOException
-    {
-        // Get first boundary
-        String line = _in.readLine();
-        if (!line.equals(_boundary))
-        {
-            //log.warn(line);
-            throw new IOException("Missing initial multi part boundary");
-        }
-        
-        // Read each part
-        while (!_lastPart)
-        {
-            // Read Part headers
-            Part part = new Part();
-            
-            String content_disposition=null;
-            while ((line=_in.readLine())!=null)
-            {
-                // If blank line, end of part headers
-                if (line.length()==0)
-                    break;
-
-                //if(log.isDebugEnabled())log.debug("LINE="+line);
-                
-                // place part header key and value in map
-                int c = line.indexOf(':',0);
-                if (c>0)
-                {
-                    String key = line.substring(0,c).trim().toLowerCase();
-                    String value = line.substring(c+1,line.length()).trim();
-                    String ev = (String) part._headers.get(key);
-                    part._headers.put(key,(ev!=null)?(ev+';'+value):value);
-                    //if(log.isDebugEnabled())log.debug(key+": "+value);
-                    if (key.equals("content-disposition"))
-                        content_disposition=value;
-                }
-            }
-
-            // Extract content-disposition
-            boolean form_data=false;
-            if (content_disposition==null)
-            {
-                throw new IOException("Missing content-disposition");
-            }
-            
-            StringTokenizer tok =
-                new StringTokenizer(content_disposition,";");
-            while (tok.hasMoreTokens())
-            {
-                String t = tok.nextToken().trim();
-                String tl = t.toLowerCase();
-                if (t.startsWith("form-data"))
-                    form_data=true;
-                else if (tl.startsWith("name="))
-                    part._name=value(t);
-                else if (tl.startsWith("filename="))
-                    part._filename=value(t);
-            }
-
-            // Check disposition
-            if (!form_data)
-            {
-                //log.warn("Non form-data part in multipart/form-data");
-                continue;
-            }
-            if (part._name==null || part._name.length()==0)
-            {
-                //log.warn("Part with no name in multipart/form-data");
-                continue;
-            }
-            //if(log.isDebugEnabled())log.debug("name="+part._name);
-            //if(log.isDebugEnabled())log.debug("filename="+part._filename);
-            _partMap.add(part._name,part);
-            part._data=readBytes();
-        }       
-    }
-
-    /* ------------------------------------------------------------ */
-    private byte[] readBytes()
-        throws IOException
-    {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-
-        int c;
-        boolean cr=false;
-        boolean lf=false;
-        
-        // loop for all lines`
-        while (true)
-        {
-            int b=0;
-            while ((c=(_char!=-2)?_char:_in.read())!=-1)
-            {
-                _char=-2;
-
-                // look for CR and/or LF
-                if (c==13 || c==10)
-                {
-                    if (c==13) _char=_in.read();
-                    break;
-                }
-
-                // look for boundary
-                if (b>=0 && b<_byteBoundary.length && c==_byteBoundary[b])
-                    b++;
-                else
-                {
-                    // this is not a boundary
-                    if (cr) baos.write(13);
-                    if (lf) baos.write(10);
-                    cr=lf=false;
-                    
-                    if (b>0)
-                        baos.write(_byteBoundary,0,b);
-                    b=-1;
-                  
-                    baos.write(c);
-                }
-            }
-
-            // check partial boundary
-            if ((b>0 && b<_byteBoundary.length-2) ||
-                (b==_byteBoundary.length-1))
-            {
-                if (cr) baos.write(13);
-                if (lf) baos.write(10);
-                cr=lf=false;
-                baos.write(_byteBoundary,0,b);
-                b=-1;
-            }
-            
-            // boundary match
-            if (b>0 || c==-1)
-            {
-                if (b==_byteBoundary.length)
-                    _lastPart=true;
-                if (_char==10) _char=-2;
-                break;
-            }
-            
-            // handle CR LF
-            if (cr) baos.write(13);
-            if (lf) baos.write(10);
-            cr=(c==13);
-            lf=(c==10 || _char==10);
-            if (_char==10) _char=-2;  
-        }
-        //if(log.isTraceEnabled())log.trace(baos.toString());
-        return baos.toByteArray();
-    }
-    
-    
-    /* ------------------------------------------------------------ */
-    private String value(String nameEqualsValue)
-    {   
-        String value =
-            nameEqualsValue.substring(nameEqualsValue.indexOf('=')+1).trim();
-        
-        int i=value.indexOf(';');
-        if (i>0)
-            value=value.substring(0,i);
-        if (value.startsWith("\""))
-        {
-            value=value.substring(1,value.indexOf('"',1));
-        }
-        
-        else
-        {
-            i=value.indexOf(' ');
-            if (i>0)
-                value=value.substring(0,i);
-        }
-        return value;
-    }
-    
-    /* ------------------------------------------------------------ */
-    private class Part
-    {
-        String _name=null;
-        String _filename=null;
-        Hashtable _headers= new Hashtable(10);
-        byte[] _data=null;
-    }    
-};
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/PostBean.java b/apps/syndie/java/src/net/i2p/syndie/web/PostBean.java
deleted file mode 100644
index 7e653587d9cc995204c57c975170663f81b95f45..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/PostBean.java
+++ /dev/null
@@ -1,226 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
-
-import net.i2p.I2PAppContext;
-import net.i2p.client.naming.PetName;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.syndie.sml.HTMLPreviewRenderer;
-import net.i2p.syndie.sml.HTMLRenderer;
-import net.i2p.util.Log;
-
-/**
- *
- */
-public class PostBean {
-    private I2PAppContext _context;
-    private Log _log;
-    private User _user;
-    private String _subject;
-    private String _tags;
-    private String _headers;
-    private String _text;
-    private String _archive;
-    private List _filenames;
-    private List _fileStreams;
-    private List _localFiles;
-    private List _fileTypes;
-    private boolean _previewed;
-    
-    public PostBean() { 
-        _context = I2PAppContext.getGlobalContext();
-        _log = _context.logManager().getLog(PostBean.class);
-        reinitialize(); 
-    }
-    
-    public void reinitialize() {
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Reinitializing " + (_text != null ? "(with " + _text.length() + " bytes of sml!)" : ""));
-        _user = null;
-        _subject = null;
-        _tags = null;
-        _text = null;
-        _headers = null;
-        _archive = null;
-        _filenames = new ArrayList();
-        _fileStreams = new ArrayList();
-        _fileTypes = new ArrayList();
-        if (_localFiles != null)
-            for (int i = 0; i < _localFiles.size(); i++)
-                ((File)_localFiles.get(i)).delete();
-        
-        _localFiles = new ArrayList();
-        _previewed = false;
-    }
-
-    public User getUser() { return _user; }
-    public String getSubject() { return (_subject != null ? _subject : ""); }
-    public String getTags() { return (_tags != null ? _tags : ""); }
-    public String getText() { return (_text != null ? _text : ""); }
-    public String getHeaders() { return (_headers != null ? _headers : ""); }
-    public void setUser(User user) { _user = user; }
-    public void setSubject(String subject) { _subject = subject; }
-    public void setTags(String tags) { _tags = tags; }
-    public void setText(String text) { _text = text; }
-    public void setHeaders(String headers) { _headers = headers; }
-    public void setArchive(String archive) { _archive = archive; }
-    
-    public String getContentType(int id) { 
-        if ( (id >= 0) && (id < _fileTypes.size()) )
-            return (String)_fileTypes.get(id);
-        return "application/octet-stream";
-    }
-    
-    public void writeAttachmentData(int id, OutputStream out) throws IOException {
-        FileInputStream in = null;
-        try {
-            in = new FileInputStream((File)_localFiles.get(id));
-            byte buf[] = new byte[1024];
-            int read = 0;
-            while ( (read = in.read(buf)) != -1) 
-                out.write(buf, 0, read);
-            out.close();
-        } finally {
-            if (in != null) try { in.close(); } catch (IOException ioe) {}
-        }
-    }
-    
-    public void addAttachment(String filename, InputStream fileStream, String mimeType) { 
-        _filenames.add(filename);
-        _fileStreams.add(fileStream);
-        _fileTypes.add(mimeType);
-    }
-    public int getAttachmentCount() { return (_filenames != null ? _filenames.size() : 0); }
-    
-    public BlogURI postEntry() throws IOException {
-        if (!_previewed) return null;
-        List localStreams = new ArrayList(_localFiles.size());
-        for (int i = 0; i < _localFiles.size(); i++) {
-            File f = (File)_localFiles.get(i);
-            localStreams.add(new FileInputStream(f));
-        }
-        BlogURI uri = BlogManager.instance().createBlogEntry(_user, _subject, _tags, _headers, _text, 
-                                                             _filenames, localStreams, _fileTypes);
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Posted the entry " + uri.toString() + " (archive = " + _archive + ")");
-        if ( (uri != null) && BlogManager.instance().authorizeRemote(_user) ) {
-            PetName pn = _user.getPetNameDB().getByName(_archive);
-            if (_log.shouldLog(Log.DEBUG))
-                _log.debug("Archive to petname? " + pn + " (protocol: " + (pn != null ? pn.getProtocol() : "") + ")");
-            if ( (pn != null) && ("syndiearchive".equals(pn.getProtocol())) ) {
-                RemoteArchiveBean r = new RemoteArchiveBean();
-                Map params = new HashMap();
-                
-                String entries[] = null;
-                BlogInfo info = BlogManager.instance().getArchive().getBlogInfo(uri);
-                if (info != null) {
-                    String str = info.getProperty(BlogInfo.SUMMARY_ENTRY_ID);
-                    if (str != null) {
-                        entries = new String[] { uri.toString(), str };
-                    }
-                }
-                if (entries == null)
-                    entries = new String[] { uri.toString() };
-                
-                params.put("localentry", entries);
-                String proxyHost = BlogManager.instance().getDefaultProxyHost();
-                String port = BlogManager.instance().getDefaultProxyPort();
-                int proxyPort = 4444;
-                try { proxyPort = Integer.parseInt(port); } catch (NumberFormatException nfe) {}
-                if (_log.shouldLog(Log.DEBUG))
-                    _log.debug("Posting the entry " + uri.toString() + " to " + pn.getLocation());
-                r.postSelectedEntries(_user, params, proxyHost, proxyPort, pn.getLocation());
-                if (_log.shouldLog(Log.DEBUG))
-                    _log.debug("Post status: " + r.getStatus());
-            }
-        }
-        return uri;
-    }
-    
-    public void renderPreview(Writer out) throws IOException {
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Subject: " + _subject);
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Text: " + _text);
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Headers: " + _headers);
-        // cache all the _fileStreams into temporary files, storing those files in _localFiles
-        // then render the page accordingly with an HTMLRenderer, altered to use a different 
-        // 'view attachment'
-        cacheAttachments();
-        String smlContent = renderSMLContent();
-        HTMLPreviewRenderer r = new HTMLPreviewRenderer(_context, _filenames, _fileTypes, _localFiles);
-        r.render(_user, BlogManager.instance().getArchive(), null, smlContent, out, false, true);
-        _previewed = true;
-    }
-    
-    public void renderReplyPreview(Writer out, String parentURI) throws IOException {
-        HTMLRenderer r = new HTMLRenderer(_context);
-        Archive a = BlogManager.instance().getArchive();
-        BlogURI uri = new BlogURI(parentURI);
-        if (uri.getEntryId() > 0) {
-            EntryContainer entry = a.getEntry(uri);
-            r.render(_user, a, entry, out, false, true);
-        }
-    }
-    
-    private String renderSMLContent() {
-        StringBuffer raw = new StringBuffer();
-        raw.append("Subject: ").append(_subject).append('\n');
-        raw.append("Tags: ");
-        StringTokenizer tok = new StringTokenizer(_tags, " \t\n");
-        while (tok.hasMoreTokens())
-            raw.append(tok.nextToken()).append('\t');
-        raw.append('\n');
-        raw.append(_headers.trim());
-        raw.append("\n\n");
-        raw.append(_text.trim());
-        return raw.toString();
-    }
-    
-    /** until we have a good filtering/preferences system, lets try to keep the content small */
-    private static final int MAX_SIZE = 256*1024;
-    
-    private void cacheAttachments() throws IOException {
-        if (_user == null) throw new IOException("User not specified");
-        File postCacheDir = new File(BlogManager.instance().getTempDir(), _user.getBlog().toBase64());
-        if (!postCacheDir.exists())
-            postCacheDir.mkdirs();
-        for (int i = 0; i < _fileStreams.size(); i++) {
-            InputStream in = (InputStream)_fileStreams.get(i);
-            File f = File.createTempFile("attachment", ".dat", postCacheDir);
-            FileOutputStream o = new FileOutputStream(f);
-            byte buf[] = new byte[1024];
-            int read = 0;
-            while ( (read = in.read(buf)) != -1) 
-                o.write(buf, 0, read);
-            o.close();
-            in.close();
-            if (f.length() > MAX_SIZE) {
-                _log.error("Refusing to post the attachment, because it is too big: " + f.length());
-            } else {
-                _localFiles.add(f);
-                if (_log.shouldLog(Log.DEBUG))
-                    _log.debug("Caching attachment " + i + " temporarily in " 
-                                   + f.getAbsolutePath() + " w/ " + f.length() + "bytes");
-            }
-        }
-        _fileStreams.clear();
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/PostServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/PostServlet.java
deleted file mode 100644
index 8d7ce38148b55c3fafa2f4e0eb2b6c5de2a8b9cb..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/PostServlet.java
+++ /dev/null
@@ -1,381 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.TreeSet;
-
-import javax.servlet.http.HttpServletRequest;
-
-import net.i2p.client.naming.PetName;
-import net.i2p.client.naming.PetNameDB;
-import net.i2p.data.Base64;
-import net.i2p.data.DataHelper;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.ThreadIndex;
-import net.i2p.syndie.sml.HTMLRenderer;
-import net.i2p.syndie.sml.ThreadedHTMLRenderer;
-
-/**
- * Post and preview form
- *
- */
-public class PostServlet extends BaseServlet {
-    public static final String PARAM_ACTION = "action";
-    public static final String ACTION_CONFIRM = "confirm";
-    
-    public static final String PARAM_SUBJECT = "entrysubject";
-    public static final String PARAM_TAGS = ThreadedHTMLRenderer.PARAM_TAGS;
-    public static final String PARAM_INCLUDENAMES = "includenames";
-    public static final String PARAM_TEXT = "entrytext";
-    public static final String PARAM_HEADERS = "entryheaders";
-    
-    public static final String PARAM_PARENT = "parentURI";
-    public static final String PARAM_IN_NEW_THREAD = "replyInNewThread";
-    public static final String PARAM_REFUSE_REPLIES = "refuseReplies";
-    
-    public static final String PARAM_REMOTE_ARCHIVE = "archive";
-    
-    private static final String ATTR_POST_BEAN = "post";
-    
-    protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, 
-                                        int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
-        if (!user.getAuthenticated()) {
-            out.write("<tr><td colspan=\"3\">You must be logged in to post</td></tr>\n");
-        } else {
-            PostBean post = getPostBean(user, req);
-            String action = req.getParameter(PARAM_ACTION);
-            if (!empty(action) && ACTION_CONFIRM.equals(action)) {
-                postEntry(user, req, archive, post, out);
-                post.reinitialize();
-                post.setUser(user);
-            } else {
-                String contentType = req.getContentType();
-                if (!empty(contentType) && (contentType.indexOf("boundary=") != -1)) {
-                    previewPostedData(user, req, archive, contentType, post, out);
-                } else {
-                    displayNewForm(user, req, post, out);
-                }
-            }
-        }
-    }
-    
-    private void previewPostedData(User user, HttpServletRequest rawRequest, Archive archive, String contentType, PostBean post, PrintWriter out) throws IOException {
-        MultiPartRequest req = new MultiPartRequest(rawRequest);
-        
-        if (!authAction(req.getString(PARAM_AUTH_ACTION))) {
-            out.write("<tr><td colspan=\"3\"><span class=\"b_postMsgErro\">Invalid form submission... stale data?</span></td></tr>");
-            return;
-        }
-        
-        // not confirmed but they posted stuff... gobble up what they give
-        // and display it as a prview (then we show the confirm form
-        
-        out.write("<tr><td colspan=\"3\">");
-        
-        //post.reinitialize();
-        //post.setUser(user);
-        
-        boolean inNewThread = getInNewThread(req.getString(PARAM_IN_NEW_THREAD));
-        boolean refuseReplies = getRefuseReplies(req.getString(PARAM_REFUSE_REPLIES));
-        
-        String entrySubject = req.getString(PARAM_SUBJECT);
-        String entryTags = req.getString(PARAM_TAGS);
-        String entryText = req.getString(PARAM_TEXT);
-        String entryHeaders = req.getString(PARAM_HEADERS);
-        String style = ""; //req.getString("style");
-        if ( (style != null) && (style.trim().length() > 0) ) {
-          if (entryHeaders == null) entryHeaders = HTMLRenderer.HEADER_STYLE + ": " + style;
-          else entryHeaders = entryHeaders + '\n' + HTMLRenderer.HEADER_STYLE + ": " + style;
-        }
-        String replyTo = req.getString(PARAM_PARENT);
-        if ( (replyTo != null) && (replyTo.trim().length() > 0) ) {
-          byte r[] = Base64.decode(replyTo);
-          if (r != null) {
-            replyTo = new String(r, "UTF-8");
-            if (!replyTo.startsWith("entry://") && !replyTo.startsWith("blog://"))
-                replyTo = "entry://" + replyTo;
-            if (entryHeaders == null) entryHeaders = HTMLRenderer.HEADER_IN_REPLY_TO + ": " + replyTo;
-            else entryHeaders = entryHeaders + '\n' + HTMLRenderer.HEADER_IN_REPLY_TO + ": " + replyTo;
-          } else {
-            replyTo = null;
-          }
-        }
-        
-        if (entryTags == null) entryTags = "";
-        
-        if ( (entryHeaders == null) || (entryHeaders.trim().length() <= 0) )
-            entryHeaders = ThreadedHTMLRenderer.HEADER_FORCE_NEW_THREAD + ": " + inNewThread + '\n' +
-                           ThreadedHTMLRenderer.HEADER_REFUSE_REPLIES + ": " + refuseReplies;
-        else
-            entryHeaders = entryHeaders.trim() + '\n' +
-                           ThreadedHTMLRenderer.HEADER_FORCE_NEW_THREAD + ": " + inNewThread + '\n' +
-                           ThreadedHTMLRenderer.HEADER_REFUSE_REPLIES + ": " + refuseReplies;
-        
-        String includeNames = req.getString(PARAM_INCLUDENAMES);
-        if ( (includeNames != null) && (includeNames.trim().length() > 0) ) {
-          PetNameDB db = user.getPetNameDB();
-          if (entryHeaders == null) entryHeaders = "";
-          for (Iterator iter = db.getNames().iterator(); iter.hasNext(); ) {
-            PetName pn = db.getByName((String)iter.next());
-            if ( (pn != null) && (pn.getIsPublic()) ) {
-              entryHeaders = entryHeaders.trim() + '\n' + HTMLRenderer.HEADER_PETNAME + ": " + 
-                             pn.getName() + "\t" + pn.getNetwork() + "\t" + pn.getProtocol() + "\t" + pn.getLocation();
-            }
-          }
-        }
-        
-        post.setSubject(entrySubject);
-        post.setTags(entryTags);
-        post.setText(entryText);
-        post.setHeaders(entryHeaders);
-
-        for (int i = 0; i < 32; i++) {
-          String filename = req.getFilename("entryfile" + i);
-          if ( (filename != null) && (filename.trim().length() > 0) ) {
-            Hashtable params = req.getParams("entryfile" + i);
-            String type = "application/octet-stream";
-            for (Iterator iter = params.keySet().iterator(); iter.hasNext(); ) {
-              String cur = (String)iter.next();
-              if ("content-type".equalsIgnoreCase(cur)) {
-                type = (String)params.get(cur);
-                break;
-              }
-            }
-            post.addAttachment(filename.trim(), req.getInputStream("entryfile" + i), type);
-          }
-        }
-
-        post.renderPreview(out);
-        out.write("<hr /><span class=\"b_postConfirm\"><form action=\"" + getPostURI() + "\" method=\"POST\">\n");
-        writeAuthActionFields(out);
-        out.write("Please confirm that the above is ok");
-        if (BlogManager.instance().authorizeRemote(user)) { 
-            out.write(", and select what additional archive you want the post transmitted to: ");
-            out.write("<select class=\"b_postConfirm\" name=\"" + PARAM_REMOTE_ARCHIVE + "\">\n");
-            PetNameDB db = user.getPetNameDB();
-            TreeSet names = new TreeSet();
-            for (Iterator iter = db.getNames().iterator(); iter.hasNext(); ) {
-              String name = (String)iter.next();
-              PetName pn = db.getByName(name);
-              if ("syndiearchive".equals(pn.getProtocol()))
-                names.add(pn.getName());
-            }
-            for (Iterator iter = names.iterator(); iter.hasNext(); ) {
-              String name = (String)iter.next();
-              out.write("<option value=\"" + HTMLRenderer.sanitizeTagParam(name) + "\">"
-                        + HTMLRenderer.sanitizeString(name) + "</option>\n");
-            }
-            out.write("<option name=\"\">None - don't push this post anywhere</option>\n");
-            
-            out.write("</select><br />\n");
-            out.write("If you don't push this post remotely now, you can do so later on the <a href=\"syndicate.jsp\">syndicate</a> screen ");
-            out.write("by choosing an archive, verifying that they don't already have the post, and selecting which posts to push.\n");
-        }
-        out.write("</span><input class=\"b_postConfirm\" type=\"submit\" name=\"" + PARAM_ACTION 
-                  + "\" value=\"" + ACTION_CONFIRM + "\" />\n");
-        
-        out.write("</form>\n");
-        
-        displayEditForm(user, req, post, out);
-        
-        out.write("</td></tr>\n");
-    }
-    
-    private void postEntry(User user, HttpServletRequest req, Archive archive, PostBean post, PrintWriter out) throws IOException {
-        if (!authAction(req)) {
-            out.write("<tr><td colspan=\"3\"><span class=\"b_postMsgErro\">Invalid form submission... stale data?</span></td></tr>");
-            return;
-        }
-        String remArchive = req.getParameter(PARAM_REMOTE_ARCHIVE);
-        post.setArchive(remArchive);
-        BlogURI uri = post.postEntry(); 
-        if (uri != null) {
-            out.write("<tr><td colspan=\"3\"><span class=\"b_postMsgOk\">Entry <a class=\"b_postOkLink\" href=\"threads.jsp?regenerateIndex=true&post=" +
-                      uri.getKeyHash().toBase64() + "/" + uri.getEntryId() + "\">posted</a>!</span></td></tr>");
-        } else {
-            out.write("<tr><td colspan=\"3\"><span class=\"b_postMsgErro\">There was an unknown error posting the entry...</span></td></tr>");
-        }
-    }
-    
-    private void displayNewForm(User user, HttpServletRequest req, PostBean post, PrintWriter out) throws IOException {
-        // logged in and not confirmed because they didn't send us anything!  
-        // give 'em a new form
-        
-        post.reinitialize();
-        post.setUser(user);
-        
-        String parentURI = req.getParameter(PARAM_PARENT);
-        
-        String subject = getParam(req, PARAM_SUBJECT);
-        
-        out.write("<form action=\"" + getPostURI() + "\" method=\"POST\" enctype=\"multipart/form-data\">\n");
-        writeAuthActionFields(out);
-        out.write("<tr><td colspan=\"3\">\n");
-        out.write("<span class=\"b_postField\">Post subject:</span> ");
-        out.write("<input type=\"text\" class=\"b_postSubject\" size=\"80\" name=\"" + PARAM_SUBJECT 
-                  + "\" value=\"" + HTMLRenderer.sanitizeTagParam(subject) + "\" title=\"One line summary\" /><br />\n");
-        out.write("<span class=\"b_postField\">Post content (in raw <a href=\"smlref.jsp\" target=\"_blank\" title=\"SML cheatsheet\">SML</a>, no headers):</span><br />\n");
-        out.write("<textarea class=\"b_postText\" rows=\"6\" cols=\"80\" name=\"" + PARAM_TEXT + "\">" + getParam(req, PARAM_TEXT) + "</textarea><br />\n");
-        out.write("<span class=\"b_postField\">SML post headers:</span><br />\n");
-        out.write("<textarea class=\"b_postHeaders\" rows=\"2\" cols=\"80\" name=\"" + PARAM_HEADERS + "\" title=\"Most people can leave this empty\" >" + getParam(req, PARAM_HEADERS) + "</textarea><br />\n");
-        
-        if ( (parentURI != null) && (parentURI.trim().length() > 0) )
-            out.write("<input type=\"hidden\" name=\"" + PARAM_PARENT + "\" value=\"" + parentURI + "\" />\n");
-
-        out.write(" Tags: ");
-        BaseServlet.writeTagField(user, getParam(req, PARAM_TAGS), out, "Optional tags to categorize your post", "No tags", false);
-        //<input type=\"text\" size=\"10\" name=\"" + PARAM_TAGS + "\" value=\"" + getParam(req, PARAM_TAGS) + "\" title=\"Optional tags to categorize your response\" /><br />\n");
-        out.write("<br />\n");
-        
-        boolean inNewThread = getInNewThread(req);
-        boolean refuseReplies = getRefuseReplies(req);
-
-        out.write("In a new thread? <input type=\"checkbox\" value=\"true\" name=\"" + PARAM_IN_NEW_THREAD + 
-                  (inNewThread ? "\" checked=\"true\" " : "\" " ) 
-                  + " title=\"If true, this will fork a new top level thread\" /><br />\n");
-        out.write("Refuse replies? <input type=\"checkbox\" value=\"true\" name=\"" + PARAM_REFUSE_REPLIES + 
-                  (refuseReplies ? "\" checked=\"true\" " : "\" " ) 
-                  + " title=\"If true, only you will be able to reply to the post\" /><br />\n");
-        
-        out.write("<span class=\"b_postField\">Include public names?</span> ");
-        out.write("<input class=\"b_postNames\" type=\"checkbox\" name=\"" + PARAM_INCLUDENAMES 
-                  + "\" value=\"true\" title=\"If true, everything marked 'public' in your addressbook is shared\" /><br />\n");
-        
-        out.write(ATTACHMENT_FIELDS);
-
-        out.write("<hr />\n");
-        out.write("<input class=\"b_postPreview\" type=\"submit\" name=\"Post\" value=\"Preview...\" /> ");
-        out.write("<input class=\"b_postReset\" type=\"reset\" value=\"Cancel\" />\n");
-        
-        if (parentURI != null) {
-            out.write("<hr /><span id=\"parentText\" class=\"b_postParent\">");
-            String decoded = DataHelper.getUTF8(Base64.decode(parentURI));
-            post.renderReplyPreview(out, "entry://" + decoded);
-            out.write("</span><hr/>\n");
-        } 
-        
-        out.write("</td></tr>\n");
-        out.write("</form>\n");
-    }
-    
-    private void displayEditForm(User user, MultiPartRequest req, PostBean post, PrintWriter out) throws IOException {
-        String parentURI = req.getString(PARAM_PARENT);
-        
-        String subject = getParam(req, PARAM_SUBJECT);
-        
-        out.write("<hr />\n");
-        out.write("<form action=\"" + getPostURI() + "\" method=\"POST\" enctype=\"multipart/form-data\">\n");
-        writeAuthActionFields(out);
-        out.write("<tr><td colspan=\"3\">\n");
-        out.write("<span class=\"b_postField\">Post subject:</span> ");
-        out.write("<input type=\"text\" class=\"b_postSubject\" size=\"80\" name=\"" + PARAM_SUBJECT 
-                  + "\" value=\"" + HTMLRenderer.sanitizeTagParam(subject) + "\" /><br />\n");
-        out.write("<span class=\"b_postField\">Post content (in raw <a href=\"smlref.jsp\" target=\"_blank\" title=\"SML cheatsheet\">SML</a>, no headers):</span><br />\n");
-        out.write("<textarea class=\"b_postText\" rows=\"6\" cols=\"80\" name=\"" + PARAM_TEXT + "\">" + getParam(req, PARAM_TEXT) + "</textarea><br />\n");
-        out.write("<span class=\"b_postField\">SML post headers:</span><br />\n");
-        out.write("<textarea class=\"b_postHeaders\" rows=\"3\" cols=\"80\" name=\"" + PARAM_HEADERS + "\">" + getParam(req, PARAM_HEADERS) + "</textarea><br />\n");
-        
-        if ( (parentURI != null) && (parentURI.trim().length() > 0) )
-            out.write("<input type=\"hidden\" name=\"" + PARAM_PARENT + "\" value=\"" + parentURI + "\" />\n");
-
-        out.write(" Tags: ");
-        //<input type=\"text\" size=\"10\" name=\"" + PARAM_TAGS + "\" value=\"" + getParam(req, PARAM_TAGS) + "\" /><br />\n");
-        out.write(" Tags: ");
-        BaseServlet.writeTagField(user, getParam(req, PARAM_TAGS), out, "Optional tags to categorize your post", "No tags", false);
-        out.write("<br />\n");
-        
-        boolean inNewThread = getInNewThread(req);
-        boolean refuseReplies = getRefuseReplies(req);
-
-        out.write("In a new thread? <input type=\"checkbox\" value=\"true\" name=\"" + PARAM_IN_NEW_THREAD + 
-                  (inNewThread ? "\" checked=\"true\" " : "\" " ) + " /><br />\n");
-        out.write("Refuse replies? <input type=\"checkbox\" value=\"true\" name=\"" + PARAM_REFUSE_REPLIES + 
-                  (refuseReplies ? "\" checked=\"true\" " : "\" " ) + " /><br />\n");
-        
-        out.write("<span class=\"b_postField\">Include public names?</span> ");
-        out.write("<input class=\"b_postNames\" type=\"checkbox\" name=\"" + PARAM_INCLUDENAMES 
-                  + "\" value=\"true\" /><br />\n");
-        
-        int newCount = 0;
-        for (int i = 0; i < 32 && newCount < 3; i++) {
-          String filename = req.getFilename("entryfile" + i);
-          if ( (filename != null) && (filename.trim().length() > 0) ) {
-              out.write("<span class=\"b_postField\">Attachment " + i + ":</span> ");
-              out.write(HTMLRenderer.sanitizeString(filename));
-              out.write("<br />");
-          } else {
-              out.write("<span class=\"b_postField\">Attachment " + i + ":</span> ");
-              out.write("<input class=\"b_postField\" type=\"file\" name=\"entryfile" + i + "\" ");
-              out.write("/><br />");
-              newCount++;
-          }
-        }
-
-        out.write("<hr />\n");
-        out.write("<input class=\"b_postPreview\" type=\"submit\" name=\"Post\" value=\"Preview...\" /> ");
-        out.write("<input class=\"b_postReset\" type=\"reset\" value=\"Cancel\" />\n");
-        
-        out.write("</form>\n");
-    }
-    
-    private boolean getInNewThread(HttpServletRequest req) {
-        return getInNewThread(req.getParameter(PARAM_IN_NEW_THREAD));
-    }
-    private boolean getInNewThread(MultiPartRequest req) {
-        return getInNewThread(getParam(req, PARAM_IN_NEW_THREAD));
-    }
-    private boolean getInNewThread(String val) {
-        boolean rv = false;
-        String inNewThread = val;
-        if ( (inNewThread != null) && (Boolean.valueOf(inNewThread).booleanValue()) )
-            rv = true;
-        return rv;
-    }
-    private boolean getRefuseReplies(HttpServletRequest req) {
-        return getRefuseReplies(req.getParameter(PARAM_REFUSE_REPLIES));
-    }
-    private boolean getRefuseReplies(MultiPartRequest req) {
-        return getRefuseReplies(getParam(req, PARAM_REFUSE_REPLIES));
-    }
-    private boolean getRefuseReplies(String val) {
-        boolean rv = false;
-        String refuseReplies = val;
-        if ( (refuseReplies != null) && (Boolean.valueOf(refuseReplies).booleanValue()) )
-            rv = true;
-        return rv;
-    }
-    
-    private PostBean getPostBean(User user, HttpServletRequest req) {
-        PostBean bean = (PostBean)req.getSession().getAttribute(ATTR_POST_BEAN);
-        if (bean == null) {
-            bean = new PostBean();
-            req.getSession().setAttribute(ATTR_POST_BEAN, bean);
-        }
-        bean.setUser(user);
-        return bean;
-    }
-    
-    private String getParam(HttpServletRequest req, String param) {
-        String val = req.getParameter(param);
-        if (val == null) val = "";
-        return val;
-    }
-    private String getParam(MultiPartRequest req, String param) {
-        String val = req.getString(param);
-        if (val == null) return "";
-        return val;
-    }
-    
-    private static final String ATTACHMENT_FIELDS = ""
-        + "<span class=\"b_postField\">Attachment 0:</span> <input class=\"b_postField\" type=\"file\" name=\"entryfile0\" /><br />"
-        + "<span class=\"b_postField\">Attachment 1:</span> <input class=\"b_postField\" type=\"file\" name=\"entryfile1\" /><br />"
-        + "<span class=\"b_postField\">Attachment 2:</span> <input class=\"b_postField\" type=\"file\" name=\"entryfile2\" /><br />"
-        + "<span class=\"b_postField\">Attachment 3:</span> <input class=\"b_postField\" type=\"file\" name=\"entryfile3\" /><br />\n";
-
-    protected String getTitle() { return "Syndie :: Post new content"; }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ProfileServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ProfileServlet.java
deleted file mode 100644
index 1c961a768d13a86b8995d69958729df17881fa12..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/ProfileServlet.java
+++ /dev/null
@@ -1,232 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-
-import javax.servlet.http.HttpServletRequest;
-
-import net.i2p.client.naming.PetName;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.FilteredThreadIndex;
-import net.i2p.syndie.data.ThreadIndex;
-import net.i2p.syndie.sml.HTMLRenderer;
-import net.i2p.syndie.sml.ThreadedHTMLRenderer;
-
-/**
- * Render the requested profile
- *
- */
-public class ProfileServlet extends BaseServlet {
-    protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, 
-                                        int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
-        Hash author = null;
-        String str = req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR);
-        if (str != null) {
-            try {
-                author = new Hash();
-                author.fromBase64(str);
-            } catch (DataFormatException dfe) {
-                author = null;
-            }
-        } else {
-            author = user.getBlog();
-        }
-        
-        String uri = req.getRequestURI();
-        
-        if (author == null) {
-            renderInvalidProfile(out);
-        } else if ( (user.getBlog() != null) && (user.getBlog().equals(author)) ) {
-            renderMyProfile(user, uri, out, archive);
-        } else {
-            renderProfile(user, uri, out, author, archive);
-        }
-    }   
-    
-    private void renderInvalidProfile(PrintWriter out) throws IOException {
-        out.write(INVALID_PROFILE);
-    }
-    
-    private void renderMyProfile(User user, String baseURI, PrintWriter out, Archive archive) throws IOException {
-        BlogInfo info = archive.getBlogInfo(user.getBlog());
-        if (info == null)
-            return;
-        
-        out.write("<!-- " + info.toString() + "-->\n");
-        out.write("<form action=\"" + baseURI + "\" method=\"POST\">\n");
-        writeAuthActionFields(out);
-        // now add the form to update
-        out.write("<tr><td colspan=\"3\">Your profile (<a href=\"configblog.jsp\">configure your blog</a>)</td></tr>\n");
-        out.write("<tr><td colspan=\"3\">Name: <input type=\"text\" name=\"" 
-                  + ThreadedHTMLRenderer.PARAM_PROFILE_NAME + "\" value=\"" 
-                  + HTMLRenderer.sanitizeTagParam(info.getProperty(BlogInfo.NAME)) + "\"></td></tr>\n");
-        out.write("<tr><td colspan=\"3\">Account description: <input type=\"text\" name=\"" 
-                  + ThreadedHTMLRenderer.PARAM_PROFILE_DESC + "\" value=\"" 
-                  + HTMLRenderer.sanitizeTagParam(info.getProperty(BlogInfo.DESCRIPTION)) + "\"></td></tr>\n");
-        out.write("<tr><td colspan=\"3\">Contact information: <input type=\"text\" name=\"" 
-                  + ThreadedHTMLRenderer.PARAM_PROFILE_URL + "\" value=\"" 
-                  + HTMLRenderer.sanitizeTagParam(info.getProperty(BlogInfo.CONTACT_URL)) + "\"></td></tr>\n");
-        out.write("<tr><td colspan=\"3\">Other attributes:<br /><textarea rows=\"3\" name=\"" 
-                  + ThreadedHTMLRenderer.PARAM_PROFILE_OTHER + "\" cols=\"60\">");
-        String props[] = info.getProperties();
-        if (props != null) {
-            for (int i = 0; i < props.length; i++) {
-                if (!BlogInfo.NAME.equals(props[i]) && 
-                    !BlogInfo.DESCRIPTION.equals(props[i]) && 
-                    !BlogInfo.EDITION.equals(props[i]) && 
-                    !BlogInfo.OWNER_KEY.equals(props[i]) && 
-                    !BlogInfo.POSTERS.equals(props[i]) && 
-                    !BlogInfo.SIGNATURE.equals(props[i]) &&
-                    !BlogInfo.CONTACT_URL.equals(props[i])) {
-                    out.write(HTMLRenderer.sanitizeString(props[i], false) + ": " 
-                              + HTMLRenderer.sanitizeString(info.getProperty(props[i]), false) + "\n");
-                }
-            }
-        }
-        out.write("</textarea></td></tr>\n");
-
-        if (user.getAuthenticated()) {
-            if ( (user.getUsername() == null) || (user.getUsername().equals(BlogManager.instance().getDefaultLogin())) ) {
-                // this is the default user, don't let them change the password
-            } else {
-                out.write("<tr><td colspan=\"3\">Old Password: <input type=\"password\" name=\"oldPassword\" /></td></tr>\n");
-                out.write("<tr><td colspan=\"3\">Password: <input type=\"password\" name=\"password\" /></td></tr>\n");
-                out.write("<tr><td colspan=\"3\">Password again: <input type=\"password\" name=\"passwordConfirm\" /></td></tr>\n");
-            }
-            if (!BlogManager.instance().authorizeRemote(user)) {
-                out.write("<tr><td colspan=\"3\">To access the remote functionality, please specify the administrative password: <br />\n" +
-                          "<input type=\"password\" name=\"adminPass\" /></td></tr>\n");
-            }
-        }
-        
-        out.write("<tr><td colspan=\"3\"><input type=\"submit\" name=\"action\" value=\"Update profile\" /></td></tr>\n");
-        out.write("</form>\n");
-    }
-    
-    private void renderProfile(User user, String baseURI, PrintWriter out, Hash author, Archive archive) throws IOException {
-        out.write("<tr><td colspan=\"3\">Profile for ");
-        PetName pn = user.getPetNameDB().getByLocation(author.toBase64());
-        String name = null;
-        BlogInfo info = archive.getBlogInfo(author);
-        if (pn != null) {
-            out.write(pn.getName());
-            name = null;
-            if (info != null)
-                name = info.getProperty(BlogInfo.NAME);
-            
-            if ( (name == null) || (name.trim().length() <= 0) )
-                name = author.toBase64().substring(0, 6);
-            
-            out.write(" (" + name + ")");
-        } else {
-            if (info != null)
-                name = info.getProperty(BlogInfo.NAME);
-            
-            if ( (name == null) || (name.trim().length() <= 0) )
-                name = author.toBase64().substring(0, 6);
-            out.write(name);
-        }
-        out.write("</a>");
-        if (info != null)
-            out.write(" [edition " + info.getEdition() + "]");
-        out.write("<br />\n");
-        out.write("<a href=\"blog.jsp" + "?" + ViewBlogServlet.PARAM_BLOG + '=' + author.toBase64()
-                  + "\" title=\"View '" + HTMLRenderer.sanitizeTagParam(name) + "'s blog\">View their blog</a> or ");
-        out.write("<a href=\"" + getControlTarget() + "?" + ThreadedHTMLRenderer.PARAM_AUTHOR
-                  + '=' + author.toBase64() + "&\">threads they have participated in</a>\n");
-        out.write("</td></tr>\n");
-        
-        out.write("<tr><td colspan=\"3\"><hr /></td></tr>\n");
-        if (pn == null) {
-            out.write("<tr><td colspan=\"3\">Not currently bookmarked.  Add them to your ");
-            String addFav = getAddToGroupLink(user, author, FilteredThreadIndex.GROUP_FAVORITE, 
-                                              baseURI, "", "", "", "", "", author.toBase64());
-            String addIgnore = getAddToGroupLink(user, author, FilteredThreadIndex.GROUP_IGNORE, 
-                                                 baseURI, "", "", "", "", "", author.toBase64());
-            out.write("<a href=\"" + addFav + "\" title=\"Threads by favorite authors are shown specially\">favorites</a> or ");
-            out.write("<a href=\"" + addIgnore + "\" title=\"Threads by ignored authors are hidden from view\">ignored</a> ");
-            out.write("</td></tr>\n");
-        } else if (pn.isMember(FilteredThreadIndex.GROUP_IGNORE)) {
-            out.write("<tr><td colspan=\"3\">Currently ignored - threads they create are hidden.</td></tr>\n");
-            String remIgnore = getRemoveFromGroupLink(user, pn.getName(), FilteredThreadIndex.GROUP_IGNORE, 
-                                                      baseURI, "", "", "", "", "", author.toBase64());
-            out.write("<tr><td colspan=\"3\"><a href=\"" + remIgnore + "\">Unignore " + pn.getName() + "</a></td></tr>\n");
-            String remCompletely = getRemoveFromGroupLink(user, pn.getName(), "", 
-                                                          baseURI, "", "", "", "", "", author.toBase64());
-            out.write("<tr><td colspan=\"3\"><a href=\"" + remCompletely + "\">Forget about " + pn.getName() + " entirely</a></td></tr>\n");
-        } else if (pn.isMember(FilteredThreadIndex.GROUP_FAVORITE)) {
-            out.write("<tr><td colspan=\"3\">Currently marked as a favorite author - threads they participate in " +
-                       "are highlighted.</td></tr>\n");
-            String remIgnore = getRemoveFromGroupLink(user, pn.getName(), FilteredThreadIndex.GROUP_FAVORITE, 
-                                                      baseURI, "", "", "", "", "", author.toBase64());
-            out.write("<tr><td colspan=\"3\"><a href=\"" + remIgnore + "\">Remove " + pn.getName() + " from the list of favorite authors</a></td></tr>\n");
-            String addIgnore = getAddToGroupLink(user, author, FilteredThreadIndex.GROUP_IGNORE, 
-                                                 baseURI, "", "", "", "", "", author.toBase64());
-            out.write("<tr><td colspan=\"3\"><a href=\"" + addIgnore + "\" title=\"Threads by ignored authors are hidden from view\">Ignore the author</a></td></tr>");
-            String remCompletely = getRemoveFromGroupLink(user, pn.getName(), "", 
-                                                          baseURI, "", "", "", "", "", author.toBase64());
-            out.write("<tr><td colspan=\"3\"><a href=\"" + remCompletely + "\">Forget about " + pn.getName() + " entirely</a></td></tr>\n");
-        } else {
-            out.write("<tr><td colspan=\"3\">Currently bookmarked.  Add them to your ");
-            String addFav = getAddToGroupLink(user, author, FilteredThreadIndex.GROUP_FAVORITE, 
-                                              baseURI, "", "", "", "", "", author.toBase64());
-            String addIgnore = getAddToGroupLink(user, author, FilteredThreadIndex.GROUP_IGNORE, 
-                                                 baseURI, "", "", "", "", "", author.toBase64());
-            out.write("<a href=\"" + addFav + "\" title=\"Threads by favorite authors are shown specially\">favorites</a> or ");
-            out.write("<a href=\"" + addIgnore + "\" title=\"Threads by ignored authors are hidden from view\">ignored</a> list</td></tr>");
-            String remCompletely = getRemoveFromGroupLink(user, pn.getName(), "", 
-                                                          baseURI, "", "", "", "", "", author.toBase64());
-            out.write("<tr><td colspan=\"3\"><a href=\"" + remCompletely + "\">Forget about " + pn.getName() + " entirely</a></td></tr>\n");
-        }
-        
-        if (info != null) {
-            String descr = info.getProperty(BlogInfo.DESCRIPTION);
-            if ( (descr != null) && (descr.trim().length() > 0) )
-                out.write("<tr><td colspan=\"3\">Account description: " + HTMLRenderer.sanitizeString(descr) + "</td></tr>\n");
-            
-            String contactURL = info.getProperty(BlogInfo.CONTACT_URL);
-            if ( (contactURL != null) && (contactURL.trim().length() > 0) )
-                out.write("<tr><td colspan=\"3\">Contact information: "
-                          + HTMLRenderer.sanitizeString(contactURL) + "</td></tr>\n");
-            
-            String props[] = info.getProperties();
-            int altCount = 0;
-            if (props != null)
-                for (int i = 0; i < props.length; i++)
-                    if (!BlogInfo.NAME.equals(props[i]) && 
-                        !BlogInfo.DESCRIPTION.equals(props[i]) && 
-                        !BlogInfo.EDITION.equals(props[i]) && 
-                        !BlogInfo.OWNER_KEY.equals(props[i]) && 
-                        !BlogInfo.POSTERS.equals(props[i]) && 
-                        !BlogInfo.SIGNATURE.equals(props[i]) &&
-                        !BlogInfo.CONTACT_URL.equals(props[i]))
-                        altCount++;
-            if (altCount > 0) {
-                for (int i = 0; i < props.length; i++) {
-                    if (!BlogInfo.NAME.equals(props[i]) && 
-                        !BlogInfo.DESCRIPTION.equals(props[i]) && 
-                        !BlogInfo.EDITION.equals(props[i]) && 
-                        !BlogInfo.OWNER_KEY.equals(props[i]) && 
-                        !BlogInfo.POSTERS.equals(props[i]) && 
-                        !BlogInfo.SIGNATURE.equals(props[i]) &&
-                        !BlogInfo.CONTACT_URL.equals(props[i])) {
-                        out.write("<tr><td colspan=\"3\">");
-                        out.write(HTMLRenderer.sanitizeString(props[i]) + ": " 
-                                  + HTMLRenderer.sanitizeString(info.getProperty(props[i])));
-                        out.write("</td></tr>\n");
-                    }
-                }
-            }
-        }
-    }
-
-    protected String getTitle() { return "Syndie :: View profile"; }
-    
-    private static final String INVALID_PROFILE = "<tr><td colspan=\"3\">The profile requested is invalid</td></tr>\n";
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/RSSServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/RSSServlet.java
deleted file mode 100644
index 2d6c2b8f12a94bed5ad8ce26e33bbdc5b5c469f2..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/RSSServlet.java
+++ /dev/null
@@ -1,134 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.StringTokenizer;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import net.i2p.I2PAppContext;
-import net.i2p.data.Base64;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.syndie.data.FilteredThreadIndex;
-import net.i2p.syndie.data.ThreadNode;
-import net.i2p.syndie.sml.HTMLRenderer;
-import net.i2p.syndie.sml.RSSRenderer;
-import net.i2p.syndie.sml.ThreadedHTMLRenderer;
-
-/**
- *
- */
-public class RSSServlet extends HttpServlet {
-    
-    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        req.setCharacterEncoding("UTF-8");
-        resp.setCharacterEncoding("UTF-8");
-        resp.setContentType("application/rss+xml");
-        
-        User user = (User)req.getSession().getAttribute("user");
-        if (user == null) {
-            String login = req.getParameter("login");
-            String pass = req.getParameter("password");
-            user = BlogManager.instance().login(login, pass); // ignore failures - user will just be unauthorized
-            if (!user.getAuthenticated()) {
-                user.invalidate();
-                user = BlogManager.instance().getDefaultUser();
-            }
-        }
-        
-        String tags = req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS);
-        Set tagSet = new HashSet();
-        if (tags != null) {
-            StringTokenizer tok = new StringTokenizer(tags, " \n\t\r");
-            while (tok.hasMoreTokens()) {
-                String tag = (String)tok.nextToken();
-                tag = tag.trim();
-                if (tag.length() > 0)
-                    tagSet.add(tag);
-            }
-        }
-        
-        int count = 10;
-        String wanted = req.getParameter("wanted");
-        if (wanted != null) {
-            try {
-                count = Integer.parseInt(wanted);
-            } catch (NumberFormatException nfe) {
-                count = 10;
-            }
-        }
-        if (count < 0) count = 10;
-        if (count > 100) count = 100;
-        
-        Archive archive = BlogManager.instance().getArchive();
-        Set authors = new HashSet();
-        String reqAuth = req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR);
-        if (reqAuth != null) {
-            byte v[] = Base64.decode(reqAuth);
-            if ( (v != null) && (v.length == Hash.HASH_LENGTH) )
-                authors.add(new Hash(v));
-        }
-        FilteredThreadIndex index = new FilteredThreadIndex(user, archive, tagSet, authors, false);
-        List entries = new ArrayList();
-        // depth first search of the most recent threads
-        for (int i = 0; i < count && i < index.getRootCount(); i++) {
-            ThreadNode node = index.getRoot(i);
-            if (node != null)
-                walkTree(entries, node);
-        }
-        
-        StringBuffer cur = new StringBuffer();
-        cur.append(req.getScheme());
-        cur.append("://");
-        cur.append(req.getServerName());
-        if (req.getServerPort() != 80)
-            cur.append(':').append(req.getServerPort());
-        cur.append(req.getContextPath()).append('/');
-        String urlPrefix = cur.toString();
-        
-        Writer out = resp.getWriter();
-        out.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
-        out.write("<rss version=\"2.0\" xmlns:media=\"http://search.yahoo.com/mrss/\">\n");
-        out.write(" <channel>\n");
-        out.write("  <title>Syndie feed</title>\n");
-        String page = urlPrefix;
-        if (tags != null)
-            page = page + "threads.jsp?" + ThreadedHTMLRenderer.PARAM_TAGS + '=' + HTMLRenderer.sanitizeXML(tags);
-        out.write("  <link>" + page +"</link>\n");
-        out.write("  <description>Summary of the latest Syndie posts</description>\n");
-        out.write("  <generator>Syndie</generator>\n");
-        
-        RSSRenderer r = new RSSRenderer(I2PAppContext.getGlobalContext());
-        for (int i = 0; i < count && i < entries.size(); i++) {
-            BlogURI uri = (BlogURI)entries.get(i);
-            EntryContainer entry = archive.getEntry(uri);
-            r.render(user, archive, entry, urlPrefix, out);
-        }
-        
-        out.write(" </channel>\n");
-        out.write("</rss>\n");
-        out.close();
-    }
-    
-    private void walkTree(List uris, ThreadNode node) {
-        if (node == null)
-            return;
-        if (uris.contains(node))
-            return;
-        uris.add(node.getEntry());
-        for (int i = 0; i < node.getChildCount(); i++)
-            walkTree(uris, node.getChild(i));
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/RemoteArchiveBean.java b/apps/syndie/java/src/net/i2p/syndie/web/RemoteArchiveBean.java
deleted file mode 100644
index 3601d4b24d0917b951bb35503cde8b75ff2073a6..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/RemoteArchiveBean.java
+++ /dev/null
@@ -1,852 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.Writer;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-
-import net.i2p.I2PAppContext;
-import net.i2p.client.naming.PetName;
-import net.i2p.client.naming.PetNameDB;
-import net.i2p.data.Base64;
-import net.i2p.data.DataHelper;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.ArchiveIndex;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.syndie.sml.HTMLRenderer;
-import net.i2p.syndie.sml.ThreadedHTMLRenderer;
-import net.i2p.util.EepGet;
-import net.i2p.util.EepGetScheduler;
-import net.i2p.util.EepPost;
-import net.i2p.util.I2PThread;
-import net.i2p.util.Log;
-
-/**
- *
- */
-public class RemoteArchiveBean {
-    private I2PAppContext _context;
-    private Log _log;
-    private String _remoteSchema;
-    private String _remoteLocation;
-    private String _proxyHost;
-    private int _proxyPort;
-    private ArchiveIndex _remoteIndex;
-    private List _statusMessages;
-    private boolean _fetchIndexInProgress;
-    private boolean _exportCapable;
-    
-    public RemoteArchiveBean() {
-        _context = I2PAppContext.getGlobalContext();
-        _log = _context.logManager().getLog(RemoteArchiveBean.class);
-        reinitialize();
-    }
-    public void reinitialize() {
-        _remoteSchema = null;
-        _remoteLocation = null;
-        _remoteIndex = null;
-        _fetchIndexInProgress = false;
-        _proxyHost = null;
-        _proxyPort = -1;
-        _exportCapable = false;
-        _statusMessages = new ArrayList();
-    }
-    
-    public String getRemoteSchema() { return _remoteSchema; }
-    public String getRemoteLocation() { return _remoteLocation; }
-    public ArchiveIndex getRemoteIndex() { return _remoteIndex; }
-    public String getProxyHost() { return _proxyHost; }
-    public int getProxyPort() { return _proxyPort; }
-    public boolean getFetchIndexInProgress() { return _fetchIndexInProgress; }
-    public String getStatus() {
-        StringBuffer buf = new StringBuffer();
-        while (_statusMessages.size() > 0)
-            buf.append(_statusMessages.remove(0)).append("\n");
-        return buf.toString();
-    }
-    
-    private boolean ignoreBlog(User user, Hash blog) {
-        if (BlogManager.instance().isBanned(blog))
-            return true;
-        PetNameDB db = user.getPetNameDB();
-        PetName pn = db.getByLocation(blog.toBase64());
-        return ( (pn!= null) && (pn.isMember("Ignore")) );
-    }
-    
-    public void fetchMetadata(User user, Map parameters) {
-        String meta = ArchiveViewerBean.getString(parameters, "blog");
-        if (meta == null) return;
-        Set blogs = new HashSet();
-        if ("ALL".equals(meta)) {
-            Set localBlogs = BlogManager.instance().getArchive().getIndex().getUniqueBlogs();
-            Set remoteBlogs = _remoteIndex.getUniqueBlogs();
-            for (Iterator iter = remoteBlogs.iterator(); iter.hasNext(); ) {
-                Hash blog = (Hash)iter.next();
-                if (!localBlogs.contains(blog)) {
-                    if (!ignoreBlog(user, blog))
-                        blogs.add(blog);
-                }
-            }
-        } else {
-            byte h[] = Base64.decode(meta.trim());
-            if (h != null) {
-                Hash blog = new Hash(h);
-                if (!ignoreBlog(user, blog))
-                    blogs.add(blog);
-            }
-        }
-        List urls = new ArrayList(blogs.size());
-        List tmpFiles = new ArrayList(blogs.size());
-        for (Iterator iter = blogs.iterator(); iter.hasNext(); ) {
-            Hash blog = (Hash)iter.next();
-            urls.add(buildMetaURL(blog));
-            try {
-                tmpFiles.add(File.createTempFile("fetchMeta", ".txt", BlogManager.instance().getTempDir()));
-            } catch (IOException ioe) {
-                _statusMessages.add("Internal error creating temporary file to fetch " + blog.toBase64() + ": " + ioe.getMessage());
-            }
-        }
-        
-        for (int i = 0; i < urls.size(); i++)
-            _statusMessages.add("Scheduling up metadata fetches for " + HTMLRenderer.sanitizeString((String)urls.get(i)));
-        fetch(urls, tmpFiles, user, new MetadataStatusListener());
-    }
-    
-    private String buildMetaURL(Hash blog) {
-        String loc = _remoteLocation.trim();
-        int root = loc.lastIndexOf('/');
-        return loc.substring(0, root + 1) +  blog.toBase64() + "/" + Archive.METADATA_FILE;
-    }
-    
-    public void fetchSelectedEntries(User user, Map parameters) {
-        String entries[] = ArchiveViewerBean.getStrings(parameters, "entry");
-        if ( (entries == null) || (entries.length <= 0) ) return;
-        List urls = new ArrayList(entries.length);
-        List tmpFiles = new ArrayList(entries.length);
-        for (int i = 0; i < entries.length; i++) {
-            BlogURI uri = new BlogURI(entries[i]);
-            if (ignoreBlog(user, uri.getKeyHash()))
-                continue;
-            urls.add(buildEntryURL(uri));
-            try {
-                tmpFiles.add(File.createTempFile("fetchBlog", ".txt", BlogManager.instance().getTempDir()));
-            } catch (IOException ioe) {
-                _statusMessages.add("Internal error creating temporary file to fetch " + HTMLRenderer.sanitizeString(entries[i]) + ": " + ioe.getMessage());
-            }
-        }
-        
-        for (int i = 0; i < urls.size(); i++)
-            _statusMessages.add("Scheduling blog post fetching for " + HTMLRenderer.sanitizeString(entries[i]));
-        fetch(urls, tmpFiles, user, new BlogStatusListener(user));
-    }
-    
-    public void fetchSelectedBulk(User user, Map parameters) {
-        fetchSelectedBulk(user, parameters, false);
-    }
-    
-    public void fetchSelectedBulk(User user, Map parameters, boolean shouldBlock) {
-        String entries[] = ArchiveViewerBean.getStrings(parameters, "entry");
-        String action = ArchiveViewerBean.getString(parameters, "action");
-        if ("Fetch all new entries".equals(action)) {
-            ArchiveIndex localIndex = BlogManager.instance().getArchive().getIndex();
-            List uris = new ArrayList();
-            List matches = new ArrayList();
-            for (Iterator iter = _remoteIndex.getUniqueBlogs().iterator(); iter.hasNext(); ) {
-                Hash blog = (Hash)iter.next();
-                if (ignoreBlog(user, blog))
-                    continue;
-                
-                _remoteIndex.selectMatchesOrderByEntryId(matches, blog, null);
-                for (int i = 0; i < matches.size(); i++) {
-                    BlogURI uri = (BlogURI)matches.get(i);
-                    if (!localIndex.getEntryIsKnown(uri))
-                        uris.add(uri);
-                }
-                matches.clear();
-            }
-            entries = new String[uris.size()];
-            for (int i = 0; i < uris.size(); i++)
-                entries[i] = ((BlogURI)uris.get(i)).toString();
-        }
-        if ( (entries == null) || (entries.length <= 0) ) return;
-        if (_exportCapable) {
-            StringBuffer url = new StringBuffer(512);
-            url.append(buildExportURL());
-            StringBuffer postData = new StringBuffer(512);
-            Set meta = new HashSet();
-            for (int i = 0; i < entries.length; i++) {
-                BlogURI uri = new BlogURI(entries[i]);
-                if (uri.getEntryId() >= 0) {
-                    postData.append("entry=").append(uri.toString()).append('&');
-                    meta.add(uri.getKeyHash());
-                    _statusMessages.add("Scheduling bulk blog post fetch of " + HTMLRenderer.sanitizeString(entries[i]));
-                }
-            }
-            for (Iterator iter = meta.iterator(); iter.hasNext(); ) {
-                Hash blog = (Hash)iter.next();
-                postData.append("meta=").append(blog.toBase64()).append('&');
-                _statusMessages.add("Scheduling bulk blog metadata fetch of " + blog.toBase64());
-            }
-            try {
-                File tmp = File.createTempFile("fetchBulk", ".zip", BlogManager.instance().getTempDir());
-                
-                boolean shouldProxy = (_proxyHost != null) && (_proxyPort > 0);
-                final EepGet get = new EepGet(_context, shouldProxy, _proxyHost, _proxyPort, 0, tmp.getAbsolutePath(), url.toString(), postData.toString());
-                get.addStatusListener(new BulkFetchListener(user, tmp));
-                
-                if (shouldBlock) {
-                    get.fetch();
-                } else {
-                    I2PThread t = new I2PThread(new Runnable() { public void run() { get.fetch(); } }, "Syndie fetcher");
-                    t.setDaemon(true);
-                    t.start();
-                }
-            } catch (IOException ioe) {
-                _statusMessages.add("Internal error creating temporary file to fetch " + HTMLRenderer.sanitizeString(url.toString()) + ": " + ioe.getMessage());
-            }
-        } else {
-            List urls = new ArrayList(entries.length+8);
-            for (int i = 0; i < entries.length; i++) {
-                BlogURI uri = new BlogURI(entries[i]);
-                if (uri.getEntryId() >= 0) {
-                    String metaURL = buildMetaURL(uri.getKeyHash());
-                    if (!urls.contains(metaURL)) {
-                        urls.add(metaURL);
-                        _statusMessages.add("Scheduling blog metadata fetch of " + HTMLRenderer.sanitizeString(entries[i]));
-                    }
-                    urls.add(buildEntryURL(uri));
-                    _statusMessages.add("Scheduling blog post fetch of " + HTMLRenderer.sanitizeString(entries[i]));
-                }
-            }
-            List tmpFiles = new ArrayList(1);
-            try {
-                for (int i = 0; i < urls.size(); i++) {
-                    File t = File.createTempFile("fetchBulk", ".dat", BlogManager.instance().getTempDir());
-                    tmpFiles.add(t);
-                }
-                fetch(urls, tmpFiles, user, new BlogStatusListener(user), shouldBlock);
-            } catch (IOException ioe) {
-                _statusMessages.add("Internal error creating temporary file to fetch posts: " + HTMLRenderer.sanitizeString(urls.toString()));
-            }
-        }
-    }
-    
-    private String buildExportURL() {
-        String loc = _remoteLocation.trim();
-        int root = loc.lastIndexOf('/');
-        return loc.substring(0, root + 1) + "export.zip?";
-    }
-    
-    private String buildEntryURL(BlogURI uri) {
-        String loc = _remoteLocation.trim();
-        int root = loc.lastIndexOf('/');
-        return loc.substring(0, root + 1) + uri.getKeyHash().toBase64() + "/" + uri.getEntryId() + ".snd";
-    }
-    
-    public void fetchAllEntries(User user, Map parameters) {
-        ArchiveIndex localIndex = BlogManager.instance().getArchive().getIndex();
-        List uris = new ArrayList();
-        List entries = new ArrayList();
-        for (Iterator iter = _remoteIndex.getUniqueBlogs().iterator(); iter.hasNext(); ) {
-            Hash blog = (Hash)iter.next();
-            if (ignoreBlog(user, blog))
-                continue;
-            _remoteIndex.selectMatchesOrderByEntryId(entries, blog, null);
-            for (int i = 0; i < entries.size(); i++) {
-                BlogURI uri = (BlogURI)entries.get(i);
-                if (!localIndex.getEntryIsKnown(uri))
-                    uris.add(uri);
-            }
-            entries.clear();
-        }
-        List urls = new ArrayList(uris.size());
-        List tmpFiles = new ArrayList(uris.size());
-        for (int i = 0; i < uris.size(); i++) {
-            urls.add(buildEntryURL((BlogURI)uris.get(i)));
-            try {
-                tmpFiles.add(File.createTempFile("fetchBlog", ".txt", BlogManager.instance().getTempDir()));
-            } catch (IOException ioe) {
-                _statusMessages.add("Internal error creating temporary file to fetch " + HTMLRenderer.sanitizeString(uris.get(i).toString()) + ": " + ioe.getMessage());
-            }
-        }
-        
-        for (int i = 0; i < urls.size(); i++)
-            _statusMessages.add("Fetch all entries: " + HTMLRenderer.sanitizeString((String)urls.get(i)));
-        fetch(urls, tmpFiles, user, new BlogStatusListener(user));
-    }
-    
-    private void fetch(List urls, List tmpFiles, User user, EepGet.StatusListener lsnr) {
-        fetch(urls, tmpFiles, user, lsnr, false);
-    }
-    
-    private void fetch(List urls, List tmpFiles, User user, EepGet.StatusListener lsnr, boolean shouldBlock) {
-        EepGetScheduler scheduler = new EepGetScheduler(I2PAppContext.getGlobalContext(), urls, tmpFiles, _proxyHost, _proxyPort, lsnr);
-        scheduler.fetch(shouldBlock);
-    }
-    
-    public void fetchIndex(User user, String schema, String location, String proxyHost, String proxyPort, boolean allowCaching) {
-        _fetchIndexInProgress = true;
-        _remoteIndex = null;
-        _remoteLocation = location;
-        _remoteSchema = schema;
-        _proxyHost = null;
-        _proxyPort = -1;
-        _exportCapable = false;
-        if (user == null) user = BlogManager.instance().getDefaultUser();
-        
-        if ( (schema == null) || (schema.trim().length() <= 0) ||
-             (location == null) || (location.trim().length() <= 0) ) {
-            _statusMessages.add("Location must be specified [" + location + "] [" + schema + "]");
-            _fetchIndexInProgress = false;
-            return;
-        }
-        
-        if ("web".equals(schema)) {
-            if ( (proxyHost != null) && (proxyHost.trim().length() > 0) &&
-            (proxyPort != null) && (proxyPort.trim().length() > 0) ) {
-                _proxyHost = proxyHost;
-                try {
-                    _proxyPort = Integer.parseInt(proxyPort);
-                } catch (NumberFormatException nfe) {
-                    _statusMessages.add("Proxy port " + HTMLRenderer.sanitizeString(proxyPort) + " is invalid");
-                    _fetchIndexInProgress = false;
-                    return;
-                }
-            }
-        } else {
-            _statusMessages.add(new String("Remote schema " + HTMLRenderer.sanitizeString(schema) + " currently not supported"));
-            _fetchIndexInProgress = false;
-            return;
-        }
-        
-        _statusMessages.add("Fetching index from " + HTMLRenderer.sanitizeString(_remoteLocation) +
-                            (_proxyHost != null ? " via " + HTMLRenderer.sanitizeString(_proxyHost) + ":" + _proxyPort : ""));
-        
-        File archiveFile;
-        if (user.getBlog() != null) {
-            archiveFile = new File(BlogManager.instance().getTempDir(), user.getBlog().toBase64() + "_remoteArchive.txt");
-        } else {
-            archiveFile = new File(BlogManager.instance().getTempDir(), "remoteArchive.txt");
-        }
-        archiveFile.delete();
-        
-        Properties etags = new Properties();
-        try {
-            DataHelper.loadProps(etags, new File(BlogManager.instance().getRootDir(), "etags"));
-        } catch (IOException ioe) {
-            //ignore
-        }
-
-        String tag = null;
-        if (allowCaching)
-            tag = etags.getProperty(location);
-        EepGet eep = new EepGet(I2PAppContext.getGlobalContext(), ((_proxyHost != null) && (_proxyPort > 0)),
-                                _proxyHost, _proxyPort, 0, archiveFile.getAbsolutePath(), location, allowCaching, tag);
-        eep.addStatusListener(new IndexFetcherStatusListener(archiveFile));
-        eep.fetch();
-        
-        if (eep.getETag() != null) { 
-            etags.setProperty(location, eep.getETag());
-        }
-        try {
-            DataHelper.storeProps(etags, new File(BlogManager.instance().getRootDir(), "etags"));
-        } catch (IOException ioe) {
-            //ignore
-        }
-    }
-    
-    private class IndexFetcherStatusListener implements EepGet.StatusListener {
-        private File _archiveFile;
-        public IndexFetcherStatusListener(File file) {
-            _archiveFile = file;
-        }
-        public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
-            _statusMessages.add("Attempt " + currentAttempt + " failed after " + bytesTransferred + (cause != null ? " " + cause.getMessage() : ""));
-        }
-        
-        public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {}
-        public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
-            _statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " successful");
-            _fetchIndexInProgress = false;
-            ArchiveIndex i = new ArchiveIndex(I2PAppContext.getGlobalContext(), false);
-            if (notModified) {
-                _statusMessages.add("Archive unchanged since last fetch.");
-                _statusMessages.add("If you want to force a refetch, make a trivial modification to the URL, such as adding a \"?\"");
-            } else {
-                try {
-                    i.load(_archiveFile);
-                    _statusMessages.add("Archive fetched and loaded");
-                    _remoteIndex = i;
-                } catch (IOException ioe) {
-                    _statusMessages.add("Archive is corrupt: " + ioe.getMessage());
-                }
-            }
-        }
-        public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
-            _statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " failed after " + bytesTransferred);
-            _fetchIndexInProgress = false;
-        }
-        public void headerReceived(String url, int currentAttempt, String key, String val) {
-            if (ArchiveServlet.HEADER_EXPORT_CAPABLE.equals(key) && ("true".equals(val))) {
-                _statusMessages.add("Remote archive is bulk export capable");
-                _exportCapable = true;
-            } else {
-                if (_log.shouldLog(Log.DEBUG))
-                    _log.debug("Header received: [" + key + "] = [" + val + "]");
-            }
-        }
-    }
-    
-    private class MetadataStatusListener implements EepGet.StatusListener {
-        public MetadataStatusListener() {}
-        public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
-            _statusMessages.add("Attempt " + currentAttempt + " failed after " + bytesTransferred + (cause != null ? " " + cause.getMessage() : ""));
-        }
-        
-        public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {}
-        public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
-            handleMetadata(url, outputFile);
-        }
-        public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
-            _statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " failed after " + bytesTransferred);;
-        }
-        public void headerReceived(String url, int currentAttempt, String key, String val) {}
-    }
-    
-    private void handleMetadata(String url, String outputFile) {
-        _statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " successful");
-        File info = new File(outputFile);
-        FileInputStream in = null;
-        try {
-            BlogInfo i = new BlogInfo();
-            in = new FileInputStream(info);
-            i.load(in);
-            boolean ok = BlogManager.instance().getArchive().storeBlogInfo(i);
-            if (ok) {
-                _statusMessages.add("Blog info for " + HTMLRenderer.sanitizeString(i.getProperty(BlogInfo.NAME)) + " imported");
-                BlogManager.instance().getArchive().reloadInfo();
-            } else {
-                _statusMessages.add("Blog info at " + HTMLRenderer.sanitizeString(url) + " was corrupt / invalid / forged");
-            }
-        } catch (IOException ioe) {
-            if (_log.shouldLog(Log.WARN))
-                _log.warn("Error handling metadata", ioe);
-        } finally {
-            if (in != null) try { in.close(); } catch (IOException ioe) {}
-            info.delete();
-        }
-    }
-    
-    private class BlogStatusListener implements EepGet.StatusListener {
-        private User _user;
-        public BlogStatusListener(User user) {
-            _user = user;
-        }
-        public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
-            _statusMessages.add("Attempt " + currentAttempt + " failed after " + bytesTransferred + (cause != null ? " " + cause.getMessage() : ""));
-        }
-        
-        public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {}
-        public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
-            if (url.endsWith(".snm")) {
-                handleMetadata(url, outputFile);
-                return;
-            }
-            _statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " successful");
-            File file = new File(outputFile);
-            FileInputStream in = null;
-            try {
-                EntryContainer c = new EntryContainer();
-                in = new FileInputStream(file);
-                c.load(in);
-                BlogURI uri = c.getURI();
-                if ( (uri == null) || (uri.getKeyHash() == null) ) {
-                    _statusMessages.add("Blog post at " + HTMLRenderer.sanitizeString(url) + " was corrupt - no URI");
-                    return;
-                }
-                Archive a = BlogManager.instance().getArchive();
-                BlogInfo info = a.getBlogInfo(uri);
-                if (info == null) {
-                    _statusMessages.add("Blog post " + uri.toString() + " cannot be imported, as we don't have their blog metadata");
-                    return;
-                }
-                boolean ok = a.storeEntry(c);
-                if (!ok) {
-                    _statusMessages.add("Blog post at " + url + ": " + uri.toString() + " has an invalid signature");
-                    return;
-                } else {
-                    _statusMessages.add("Blog post " + uri.toString() + " imported");
-                    BlogManager.instance().getArchive().regenerateIndex();
-                    _user.dataImported();
-                }
-            } catch (IOException ioe) {
-                if (_log.shouldLog(Log.WARN))
-                    _log.warn("Error importing", ioe);
-            } finally {
-                if (in != null) try { in.close(); } catch (IOException ioe) {}
-                file.delete();
-            }
-        }
-        public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
-            _statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " failed after " + bytesTransferred);
-        }
-        public void headerReceived(String url, int currentAttempt, String key, String val) {}
-    }
-    
-    /**
-     * Receive the status of a fetch for the zip containing blogs and metadata (as generated by
-     * the ExportServlet)
-     */
-    private class BulkFetchListener implements EepGet.StatusListener {
-        private File _tmp;
-        private User _user;
-        public BulkFetchListener(User user, File tmp) {
-            _user = user;
-            _tmp = tmp;
-        }
-        public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
-            _statusMessages.add("Attempt " + currentAttempt + " failed after " + bytesTransferred + (cause != null ? " " + cause.getMessage() : ""));
-        }
-        
-        public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {}
-        public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
-            _statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url.substring(0, url.indexOf('?'))) + " successful, importing the data");
-            File file = new File(outputFile);
-            ZipInputStream zi = null;
-            try {
-                zi = new ZipInputStream(new FileInputStream(file));
-                boolean postImported = false;
-                while (true) {
-                    ZipEntry entry = zi.getNextEntry();
-                    if (entry == null)
-                        break;
-
-                    ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
-                    byte buf[] = new byte[1024];
-                    int read = -1;
-                    while ( (read = zi.read(buf)) != -1) 
-                        out.write(buf, 0, read);
-
-                    if (entry.getName().startsWith("meta")) {
-                        BlogInfo i = new BlogInfo();
-                        i.load(new ByteArrayInputStream(out.toByteArray()));
-                        boolean ok = BlogManager.instance().getArchive().storeBlogInfo(i);
-                        if (ok) {
-                            _statusMessages.add("Blog info for " + HTMLRenderer.sanitizeString(i.getProperty(BlogInfo.NAME)) + " imported");
-                        } else {
-                            _statusMessages.add("Blog info at " + HTMLRenderer.sanitizeString(url) + " was corrupt / invalid / forged");
-                        }
-                    } else if (entry.getName().startsWith("entry")) {
-                        EntryContainer c = new EntryContainer();
-                        c.load(new ByteArrayInputStream(out.toByteArray()));
-                        BlogURI uri = c.getURI();
-                        if ( (uri == null) || (uri.getKeyHash() == null) ) {
-                            _statusMessages.add("Blog post " + HTMLRenderer.sanitizeString(entry.getName()) + " was corrupt - no URI");
-                            continue;
-                        }
-                        Archive a = BlogManager.instance().getArchive();
-                        BlogInfo info = a.getBlogInfo(uri);
-                        if (info == null) {
-                            _statusMessages.add("Blog post " + HTMLRenderer.sanitizeString(entry.getName()) + " cannot be imported, as we don't have their blog metadata");
-                            continue;
-                        }
-                        boolean ok = a.storeEntry(c);
-                        if (!ok) {
-                            _statusMessages.add("Blog post " + uri.toString() + " has an invalid signature");
-                            continue;
-                        } else {
-                            _statusMessages.add("Blog post " + uri.toString() + " imported");
-                            postImported = true;
-                        }
-                    }
-                }       
-                
-                BlogManager.instance().getArchive().regenerateIndex();
-                if (postImported)
-                    _user.dataImported();
-            } catch (IOException ioe) {
-                if (_log.shouldLog(Log.WARN))
-                    _log.debug("Error importing", ioe);
-                _statusMessages.add("Error importing from " + HTMLRenderer.sanitizeString(url) + ": " + ioe.getMessage());
-            } finally {
-                if (zi != null) try { zi.close(); } catch (IOException ioe) {}
-                file.delete();
-            }
-        }
-        public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
-            _statusMessages.add("Fetch of " + HTMLRenderer.sanitizeString(url) + " failed after " + bytesTransferred);
-            _tmp.delete();
-        }
-        public void headerReceived(String url, int currentAttempt, String key, String val) {}
-    }
-    
-    public void postSelectedEntries(User user, Map parameters) {
-        postSelectedEntries(user, parameters, _proxyHost, _proxyPort, _remoteLocation);
-    }
-    public void postSelectedEntries(User user, Map parameters, String proxyHost, int proxyPort, String location) {
-        String entries[] = ArchiveViewerBean.getStrings(parameters, "localentry");
-        if ( (entries == null) || (entries.length <= 0) ) return;
-        List uris = new ArrayList(entries.length);
-        for (int i = 0; i < entries.length; i++)
-            uris.add(new BlogURI(entries[i]));
-        postSelectedEntries(user, uris, proxyHost, proxyPort, location);
-    }
-    public void postSelectedEntries(User user, List uris, String location) {
-        postSelectedEntries(user, uris, _proxyHost, _proxyPort, location);
-    }
-    public void postSelectedEntries(User user, List uris, String proxyHost, int proxyPort, String location) {
-        if ( (proxyPort > 0) && (proxyHost != null) && (proxyHost.trim().length() > 0) ) {
-            _proxyPort = proxyPort;
-            _proxyHost = proxyHost;
-        } else {
-            _proxyPort = -1;
-            _proxyHost = null;
-        }
-        _remoteLocation = location;
-        post(uris, user);
-    }
-    
-    private void post(List blogURIs, User user) {
-        List files = new ArrayList(blogURIs.size()+1);
-        Set meta = new HashSet(4);
-        Map uploads = new HashMap(files.size());
-        String importURL = getImportURL();
-        _statusMessages.add("Uploading through " + HTMLRenderer.sanitizeString(importURL));
-        for (int i = 0; i < blogURIs.size(); i++) {
-            BlogURI uri = (BlogURI)blogURIs.get(i);
-            File blogDir = new File(BlogManager.instance().getArchive().getArchiveDir(), uri.getKeyHash().toBase64());
-            BlogInfo info = BlogManager.instance().getArchive().getBlogInfo(uri);
-            if (!meta.contains(uri.getKeyHash())) {
-                uploads.put("blogmeta" + meta.size(), new File(blogDir, Archive.METADATA_FILE));
-                meta.add(uri.getKeyHash());
-                _statusMessages.add("Scheduling upload of the blog metadata for " + HTMLRenderer.sanitizeString(info.getProperty(BlogInfo.NAME)));
-            }
-            uploads.put("blogpost" + i, new File(blogDir, uri.getEntryId() + ".snd"));
-            _statusMessages.add("Scheduling upload of " + HTMLRenderer.sanitizeString(info.getProperty(BlogInfo.NAME)) 
-                                + ": " + getEntryDate(uri.getEntryId()));
-        }
-        EepPost post = new EepPost();
-        post.postFiles(importURL, _proxyHost, _proxyPort, uploads, new Runnable() { public void run() { _statusMessages.add("Upload complete"); } });
-    }
-    
-    private String getImportURL() {
-        String loc = _remoteLocation.trim();
-        int archiveRoot = loc.lastIndexOf('/');
-        int syndieRoot = loc.lastIndexOf('/', archiveRoot-1);
-        return loc.substring(0, syndieRoot + 1) + "import.jsp";
-    }
-    
-    public void renderDeltaForm(User user, ArchiveIndex localIndex, Writer out) throws IOException {
-        Archive archive = BlogManager.instance().getArchive();
-        StringBuffer buf = new StringBuffer(512);
-        buf.append("<em class=\"b_remMeta\">New blogs:</em> <select class=\"b_remMeta\"name=\"blog\"><option value=\"ALL\">All</option>\n");
-        Set localBlogs = archive.getIndex().getUniqueBlogs();
-        Set remoteBlogs = _remoteIndex.getUniqueBlogs();
-        int newBlogs = 0;
-        for (Iterator iter = remoteBlogs.iterator(); iter.hasNext(); ) {
-            Hash blog = (Hash)iter.next();
-            if ( (blog == null) || (blog.getData() == null) || (blog.getData().length <= 0) )
-                continue;
-            if (ignoreBlog(user, blog))
-                continue;
-            if (!localBlogs.contains(blog)) {
-                buf.append("<option value=\"" + blog.toBase64() + "\">" + blog.toBase64() + "</option>\n");
-                newBlogs++;
-            }
-        }
-        if (newBlogs > 0) {
-            out.write(buf.toString());
-            out.write("</select> <input class=\"b_remMetaFetch\" type=\"submit\" name=\"action\" value=\"Fetch metadata\" /><br />\n");
-        }
-        
-        int newEntries = 0;
-        int localNew = 0;
-        out.write("<table class=\"b_remDelta\" border=\"1\" width=\"100%\">\n");
-        List entries = new ArrayList();
-        for (Iterator iter = remoteBlogs.iterator(); iter.hasNext(); ) {
-            Hash blog = (Hash)iter.next();
-            if (ignoreBlog(user, blog))
-                continue;
-            buf.setLength(0);
-            int shownEntries = 0;
-            buf.append("<tr class=\"b_remBlog\"><td class=\"b_remBlog\" colspan=\"5\" align=\"left\" valign=\"top\">\n");
-            BlogInfo info = archive.getBlogInfo(blog);
-            if (info != null) {
-                buf.append(HTMLRenderer.sanitizeString(info.getProperty(BlogInfo.NAME))).append(": ");
-                buf.append("<span class=\"b_remBlogDesc\">").append(HTMLRenderer.sanitizeString(info.getProperty(BlogInfo.DESCRIPTION)));
-                buf.append("</span>\n");
-            } else {
-                buf.append("<span class=\"b_remBlog\">" + blog.toBase64() + "</span>\n");
-            }
-            buf.append("</td></tr>\n");
-            buf.append("<tr class=\"b_remHeader\"><td class=\"b_remHeader\">&nbsp;</td><td class=\"b_remHeader\" nowrap=\"nowrap\">");
-            buf.append("<em class=\"b_remHeader\">Posted on</em></td>");
-            buf.append("<td class=\"b_remHeader\" nowrap=\"nowrap\"><em class=\"b_remHeader\">#</em></td>");
-            buf.append("<td class=\"b_remHeader\" nowrap=\"nowrap\"><em class=\"b_remHeader\">Size</em></td>");
-            buf.append("<td class=\"b_remHeader\" width=\"90%\" nowrap=\"true\"><em class=\"b_remHeader\">Tags</em></td></tr>\n");
-            entries.clear();
-            _remoteIndex.selectMatchesOrderByEntryId(entries, blog, null);
-            for (int i = 0; i < entries.size(); i++) {
-                BlogURI uri = (BlogURI)entries.get(i);
-                buf.append("<tr class=\"b_remDetail\">\n");
-                if (!archive.getIndex().getEntryIsKnown(uri)) {
-                    buf.append("<td class=\"b_remDetail\"><input class=\"b_remSelect\" type=\"checkbox\" name=\"entry\" value=\"" + uri.toString() + "\" /></td>\n");
-                    newEntries++;
-                    shownEntries++;
-                } else {
-                    String page = "threads.jsp?" + ThreadedHTMLRenderer.PARAM_VIEW_POST + '=' + blog.toBase64() + '/' + uri.getEntryId();
-                    buf.append("<td class=\"b_remDetail\"><a class=\"b_remLocal\" href=\"" + page + "\">(local)</a></td>\n");
-                }
-                buf.append("<td class=\"b_remDetail\"><span class=\"b_remDate\">" + getDate(uri.getEntryId()) + "</span></td>\n");
-                buf.append("<td class=\"b_remDetail\"><span class=\"b_remNum\">" + getId(uri.getEntryId()) + "</span></td>\n");
-                buf.append("<td class=\"b_remDetail\"><span class=\"b_remSize\">" + _remoteIndex.getBlogEntrySizeKB(uri) + "KB</span></td>\n");
-                buf.append("<td class=\"b_remDetail\">");
-                for (Iterator titer = new TreeSet(_remoteIndex.getBlogEntryTags(uri)).iterator(); titer.hasNext(); ) {
-                    String tag = (String)titer.next();
-                    String page = "threads.jsp?" + ThreadedHTMLRenderer.PARAM_TAGS + '=' + HTMLRenderer.sanitizeTagParam(tag);
-                    buf.append("<a class=\"b_remTag\" href=\"" + page + "\">" + HTMLRenderer.sanitizeString(tag) + "</a> \n");
-                }
-                buf.append("</td>\n");
-                buf.append("</tr>\n");
-            }
-            if (shownEntries > 0) {
-                out.write(buf.toString());
-                buf.setLength(0);
-            }
-            int remote = shownEntries;
-            
-            // now for posts in known blogs that we have and they don't
-            entries.clear();
-            localIndex.selectMatchesOrderByEntryId(entries, blog, null);
-            buf.append("<tr class=\"b_remLocalHeader\"><td class=\"b_remLocalHeader\" colspan=\"5\"><span class=\"b_remLocalHeader\">Entries we have, but the remote Syndie doesn't:</span></td></tr>\n");
-            for (int i = 0; i < entries.size(); i++) {
-                BlogURI uri = (BlogURI)entries.get(i);
-                if (!_remoteIndex.getEntryIsKnown(uri)) {
-                    buf.append("<tr class=\"b_remLocalDetail\">\n");
-                    buf.append("<td class=\"b_remLocalDetail\"><input class=\"b_remLocalSend\" type=\"checkbox\" name=\"localentry\" value=\"" + uri.toString() + "\" /></td>\n");
-                    shownEntries++;
-                    newEntries++;
-                    localNew++;
-                    buf.append("<td class=\"b_remLocalDate\"><span class=\"b_remLocalDate\">" + getDate(uri.getEntryId()) + "</span></td>\n");
-                    buf.append("<td class=\"b_remLocalNum\"><span class=\"b_remLocalNum\">" + getId(uri.getEntryId()) + "</span></td>\n");
-                    buf.append("<td class=\"b_remLocalSize\"><span class=\"b_remLocalSize\">" + localIndex.getBlogEntrySizeKB(uri) + "KB</span></td>\n");
-                    buf.append("<td class=\"b_remLocalTags\">");
-                    for (Iterator titer = new TreeSet(localIndex.getBlogEntryTags(uri)).iterator(); titer.hasNext(); ) {
-                        String tag = (String)titer.next();
-                        String page = "threads.jsp?" + ThreadedHTMLRenderer.PARAM_TAGS + '=' + HTMLRenderer.sanitizeTagParam(tag);
-                        buf.append("<a class=\"b_remLocalTag\" href=\"" + page + "\">" + HTMLRenderer.sanitizeString(tag) + "</a> \n");
-                    }
-                    buf.append("</td>\n");
-                    buf.append("</tr>\n");
-                }
-            }
-            
-            if (shownEntries > remote) // skip blogs we have already syndicated
-                out.write(buf.toString());
-        }
-
-        // now for posts in blogs we have and they don't
-        int newBefore = localNew;
-        buf.setLength(0);
-        buf.append("<tr class=\"b_remLocalHeader\"><td class=\"b_remLocalHeader\" colspan=\"5\"><span class=\"b_remLocalHeader\">Blogs the remote Syndie doesn't have</span></td></tr>\n");
-        for (Iterator iter = localBlogs.iterator(); iter.hasNext(); ) {
-            Hash blog = (Hash)iter.next();
-            if (remoteBlogs.contains(blog)) {
-                //System.err.println("Remote index has " + blog.toBase64());
-                continue;
-            } else if (ignoreBlog(user, blog)) {
-                continue;
-            }
-            
-            entries.clear();
-            localIndex.selectMatchesOrderByEntryId(entries, blog, null);
-            
-            for (int i = 0; i < entries.size(); i++) {
-                BlogURI uri = (BlogURI)entries.get(i);
-                buf.append("<tr class=\"b_remLocalDetail\">\n");
-                buf.append("<td class=\"b_remLocalDetail\"><input class=\"b_remLocalSend\" type=\"checkbox\" name=\"localentry\" value=\"" + uri.toString() + "\" /></td>\n");
-                buf.append("<td class=\"b_remLocalDate\"><span class=\"b_remLocalDate\">" + getDate(uri.getEntryId()) + "</span></td>\n");
-                buf.append("<td class=\"b_remLocalNum\"><span class=\"b_remLocalNum\">" + getId(uri.getEntryId()) + "</span></td>\n");
-                buf.append("<td class=\"b_remLocalSize\"><span class=\"b_remLocalSize\">" + localIndex.getBlogEntrySizeKB(uri) + "KB</span></td>\n");
-                buf.append("<td class=\"b_remLocalTags\">");
-                for (Iterator titer = new TreeSet(localIndex.getBlogEntryTags(uri)).iterator(); titer.hasNext(); ) {
-                    String tag = (String)titer.next();
-                    String page = "threads.jsp?" + ThreadedHTMLRenderer.PARAM_TAGS + '=' + HTMLRenderer.sanitizeTagParam(tag);
-                    buf.append("<a class=\"b_remLocalTag\" href=\"" + page + "\">" + HTMLRenderer.sanitizeString(tag) + "</a> \n");
-                }
-                buf.append("</td>\n");
-                buf.append("</tr>\n");
-                localNew++;
-            }
-        }
-        if (localNew > newBefore)
-            out.write(buf.toString());
-        
-        out.write("</table>\n");
-        if (newEntries > 0) {
-            out.write("<input class=\"b_remFetchSelected\" type=\"submit\" name=\"action\" value=\"Fetch selected entries\" /> \n");
-            out.write("<input class=\"b_remFetchAll\" type=\"submit\" name=\"action\" value=\"Fetch all new entries\" /> \n");
-        } else {
-            out.write("<span class=\"b_remNoRemotePosts\">" + HTMLRenderer.sanitizeString(_remoteLocation) + " has no new posts to offer us</span>\n");
-        }
-        if (localNew > 0) {
-            out.write("<input class=\"b_remPostSelected\" type=\"submit\" name=\"action\" value=\"Post selected entries\" /> \n");
-        }
-        out.write("<hr />\n");
-    }
-    private final SimpleDateFormat _dateFormat = new SimpleDateFormat("yyyy/MM/dd", Locale.UK);
-    private String getDate(long when) {
-        synchronized (_dateFormat) {
-            return _dateFormat.format(new Date(when));
-        }
-    }
-    private final String getEntryDate(long when) {
-        synchronized (_dateFormat) {
-            try {
-                String str = _dateFormat.format(new Date(when));
-                long dayBegin = _dateFormat.parse(str).getTime();
-                return str + "." + (when - dayBegin);
-            } catch (ParseException pe) {
-                pe.printStackTrace();
-                // wtf
-                return "unknown";
-            }
-        }
-    }
-    
-    private long getId(long id) {
-        synchronized (_dateFormat) {
-            try {
-                String str = _dateFormat.format(new Date(id));
-                long dayBegin = _dateFormat.parse(str).getTime();
-                return (id - dayBegin);
-            } catch (ParseException pe) {
-                pe.printStackTrace();
-                // wtf
-                return id;
-            }
-        }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/RunStandalone.java b/apps/syndie/java/src/net/i2p/syndie/web/RunStandalone.java
deleted file mode 100644
index 778d3e41a3150c61c6f4a2524704919baba3cbb4..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/RunStandalone.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.File;
-
-import net.i2p.util.FileUtil;
-
-import org.mortbay.jetty.Server;
-
-public class RunStandalone {
-    private Server _server;
-    
-    static {
-        System.setProperty("org.mortbay.http.Version.paranoid", "true");
-        System.setProperty("org.mortbay.xml.XmlParser.NotValidating", "true");
-        System.setProperty("syndie.rootDir", ".");
-        System.setProperty("syndie.defaultSingleUserArchives", "http://syndiemedia.i2p.net:8000/archive/archive.txt");
-        System.setProperty("syndie.defaultProxyHost", "");
-        System.setProperty("syndie.defaultProxyPort", "");
-    }
-    
-    private RunStandalone(String args[]) {}
-    
-    public static void main(String args[]) {
-        RunStandalone runner = new RunStandalone(args);
-        runner.start();
-    }
-    
-    public void start() {
-        File workDir = new File("work");
-        boolean workDirRemoved = FileUtil.rmdir(workDir, false);
-        if (!workDirRemoved)
-            System.err.println("ERROR: Unable to remove Jetty temporary work directory");
-        boolean workDirCreated = workDir.mkdirs();
-        if (!workDirCreated)
-            System.err.println("ERROR: Unable to create Jetty temporary work directory");
-        
-        try {
-            _server = new Server("jetty-syndie.xml");
-            _server.start();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-    
-    public void stop() {
-        try {
-            _server.stop();
-        } catch (InterruptedException ie) {
-            ie.printStackTrace();
-        }
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/SwitchServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/SwitchServlet.java
deleted file mode 100644
index 44fd4e970814d4fc2e3a7da0925b6ff4ae4f8152..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/SwitchServlet.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-
-import javax.servlet.http.HttpServletRequest;
-
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.ThreadIndex;
-import net.i2p.syndie.sml.ThreadedHTMLRenderer;
-
-/**
- * Login/register form
- *
- */
-public class SwitchServlet extends BaseServlet { 
-    protected String getTitle() { return "Syndie :: Login/Register"; }
-    
-    protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, 
-                                        int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
-        out.write("<form action=\"" + getControlTarget() + "\" method=\"POST\">\n");
-        writeAuthActionFields(out);
-        out.write("<tr><td colspan=\"3\"><b>Log in to an existing account</b></td></tr>\n" +
-                  "<tr><td colspan=\"3\">Login: <input type=\"text\" name=\"login\" /></td></tr>\n" +
-                  "<tr><td colspan=\"3\">Password: <input type=\"password\" name=\"password\" /></td></tr>\n" +
-                  "<tr><td colspan=\"3\"><input type=\"submit\" name=\"action\" value=\"Login\" />\n" +
-                  "<input type=\"submit\" name=\"action\" value=\"Cancel\" />\n" +
-                  "<input type=\"submit\" name=\"action\" value=\"Logout\" /></td></tr>\n" +
-                  "</form>\n" +
-                  "<tr><td colspan=\"3\"><hr /></td></tr>\n" +
-                  "<form action=\"" + ThreadedHTMLRenderer.buildProfileURL(null) + "\" method=\"POST\">\n");
-        writeAuthActionFields(out);
-        out.write("<tr><td colspan=\"3\"><b>Register a new account</b></td></tr>\n" +
-                  "<tr><td colspan=\"3\">Login: <input type=\"text\" name=\"login\" /> (only known locally)</td></tr>\n" +
-                  "<tr><td colspan=\"3\">Password: <input type=\"password\" name=\"password\" /></td></tr>\n" +
-                  "<tr><td colspan=\"3\">Public name: <input type=\"text\" name=\"accountName\" /></td></tr>\n" +
-                  "<tr><td colspan=\"3\">Description: <input type=\"text\" name=\"description\" /></td></tr>\n" +
-                  "<tr><td colspan=\"3\">Contact URL: <input type=\"text\" name=\"contactURL\" /></td></tr>\n" +
-                  "<tr><td colspan=\"3\">Registration password: <input type=\"password\" name=\"registrationPass\" />" +
-                  " (only necessary if the Syndie administrator requires it)</td></tr>\n" +
-                  "<tr><td colspan=\"3\"><input type=\"submit\" name=\"action\" value=\"Register\" /></td></tr>\n" +
-                  "</form>\n");
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/SyndicateServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/SyndicateServlet.java
deleted file mode 100644
index 337ac5aa13229198d2e9dd2c40dfdc81421c3dea..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/SyndicateServlet.java
+++ /dev/null
@@ -1,154 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Iterator;
-
-import javax.servlet.http.HttpServletRequest;
-
-import net.i2p.client.naming.PetName;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.ThreadIndex;
-import net.i2p.syndie.sml.HTMLRenderer;
-
-/**
- * Syndicate with another remote Syndie node
- *
- */
-public class SyndicateServlet extends BaseServlet { 
-    protected String getTitle() { return "Syndie :: Syndicate"; }
-    
-    public static final String PARAM_SCHEMA = "schema";
-    public static final String PARAM_LOCATION = "location";
-    public static final String PARAM_PETNAME = "petname";
-    
-    protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, 
-                                        int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
-        if (!BlogManager.instance().authorizeRemote(user)) { 
-            out.write("<tr><td colspan=\"3\">Sorry, you are not authorized to access remote archives</td></tr>\n");
-        } else {
-            out.write("<form action=\"" + req.getRequestURI() + "\" method=\"POST\">");
-            displayForm(user, req, out);
-            handleRequest(user, req, index, out);
-            out.write("</form>\n");
-        }            
-    }
-    
-    private void handleRequest(User user, HttpServletRequest req, ThreadIndex index, PrintWriter out) throws IOException {
-        RemoteArchiveBean remote = getRemote(req);
-        String action = req.getParameter("action");
-        if ("Continue...".equals(action)) {
-            String location = req.getParameter(PARAM_LOCATION);
-            String pn = req.getParameter(PARAM_PETNAME);
-            if ( (pn != null) && (pn.trim().length() > 0) ) {
-                PetName pnval = user.getPetNameDB().getByName(pn);
-                if (pnval != null) location = pnval.getLocation();
-            }
-            
-            // dont allow caching if they explicit ask for a fetch
-            boolean allowCaching = false;
-            remote.fetchIndex(user, req.getParameter(PARAM_SCHEMA), location, 
-                              req.getParameter("proxyhost"), 
-                              req.getParameter("proxyport"), allowCaching);
-        } else if ("Fetch metadata".equals(action)) {
-            remote.fetchMetadata(user, req.getParameterMap());
-        } else if ("Fetch selected entries".equals(action)) {
-            //remote.fetchSelectedEntries(user, request.getParameterMap());
-            remote.fetchSelectedBulk(user, req.getParameterMap());
-        } else if ("Fetch all new entries".equals(action)) {
-            //remote.fetchAllEntries(user, request.getParameterMap());
-            remote.fetchSelectedBulk(user, req.getParameterMap());
-        } else if ("Post selected entries".equals(action)) {
-            remote.postSelectedEntries(user, req.getParameterMap());
-        }
-        String msgs = remote.getStatus();
-        if ( (msgs != null) && (msgs.length() > 0) ) { 
-            out.write("<pre class=\"b_remoteProgress\">");
-            out.write(msgs);
-            out.write("<a class=\"b_remoteProgress\" href=\"");
-            out.write(req.getRequestURI());
-            out.write("\">Refresh</a></pre><br />\n");
-        }
-        
-        if (remote.getFetchIndexInProgress()) { 
-            out.write("<span class=\"b_remoteProgress\">Please wait while the index is being fetched ");
-            out.write("from ");
-            out.write(remote.getRemoteLocation());
-            out.write(".</span>");
-        } else if (remote.getRemoteIndex() != null) {
-            // remote index is NOT null!
-            out.write("<span class=\"b_remoteLocation\">");
-            out.write(remote.getRemoteLocation());
-            out.write("</span>");
-            out.write("<a class=\"b_remoteRefetch\" href=\"");
-            out.write(req.getRequestURI());
-            out.write("?" + PARAM_SCHEMA + "=" + remote.getRemoteSchema() + "&" + PARAM_LOCATION + "=" + remote.getRemoteLocation());
-            if (remote.getProxyHost() != null && remote.getProxyPort() > 0) { 
-                out.write("&proxyhost=" + remote.getProxyHost() + "&proxyport=" + remote.getProxyPort());
-            } 
-            out.write("&action=Continue...\">(refetch)</a>:<br />\n");
-            
-            remote.renderDeltaForm(user, BlogManager.instance().getArchive().getIndex(), out);
-            out.write("<textarea class=\"b_remoteIndex\" rows=\"5\" cols=\"120\">" + 
-                      remote.getRemoteIndex().toString() + "</textarea>");
-        }
-        
-        out.write("</td></tr>\n");
-    }
-
-    private void displayForm(User user, HttpServletRequest req, PrintWriter out) throws IOException {
-        writeAuthActionFields(out);
-        out.write("<tr><td colspan=\"3\">");
-        out.write("<span class=\"b_remoteChooser\"><span class=\"b_remoteChooserField\">Import from:</span>\n");
-        out.write("<select class=\"b_remoteChooserNet\" name=\"" + PARAM_SCHEMA + "\">\n");
-        String schema = req.getParameter(PARAM_SCHEMA);
-        out.write("<option value=\"web\" ");
-        if ("web".equals(schema))
-            out.write("selected=\"true\" ");
-        out.write(">I2P/Web/Tor/Freenet</option>\n");
-
-        out.write("</select>\n");
-        out.write("<span class=\"b_remoteChooserField\">Proxy</span>\n");
-        out.write("<input class=\"b_remoteChooserHost\" type=\"text\" size=\"12\" name=\"proxyhost\" value=\"");
-        out.write(BlogManager.instance().getDefaultProxyHost());
-        out.write("\" title=\"hostname that your HTTP proxy is on, or blank for no proxy\" />\n");
-        out.write("<input class=\"b_remoteChooserPort\" type=\"text\" size=\"4\" name=\"proxyport\" value=\"");
-        out.write(BlogManager.instance().getDefaultProxyPort());
-        out.write("\" title=\"port number that your HTTP proxy is on, or blank for no proxy\" /><br />\n");
-        out.write("<span class=\"b_remoteChooserField\">Bookmarked archives:</span>\n");
-        out.write("<select class=\"b_remoteChooserPN\" name=\"" + PARAM_PETNAME + "\">");
-        out.write("<option value=\"\">Custom location</option>");
-
-        for (Iterator iter = user.getPetNameDB().iterator(); iter.hasNext(); ) {
-            PetName pn = (PetName)iter.next();
-            if (AddressesServlet.PROTO_ARCHIVE.equals(pn.getProtocol())) {
-                out.write("<option value=\"");
-                out.write(HTMLRenderer.sanitizeTagParam(pn.getName()));
-                out.write("\">");
-                out.write(HTMLRenderer.sanitizeString(pn.getName()));
-                out.write("</option>");
-            }
-        }
-        out.write("</select> or ");
-        out.write("<input type=\"text\" class=\"b_remoteChooserLocation\" name=\"" + PARAM_LOCATION + "\" size=\"30\" value=\"");
-        String reqLoc = req.getParameter("location");
-        if (reqLoc != null)
-            out.write(reqLoc);
-        out.write("\" title=\"full URL to the remote location, to be sent to your HTTP proxy\" />\n");
-        out.write("<input class=\"b_remoteChooserContinue\" type=\"submit\" name=\"action\" value=\"Continue...\" /><br />\n");
-        out.write("</span>\n");
-    }
-
-    private static final String ATTR_REMOTE = "remote";
-    protected RemoteArchiveBean getRemote(HttpServletRequest req) {
-        RemoteArchiveBean remote = (RemoteArchiveBean)req.getSession().getAttribute(ATTR_REMOTE);
-        if (remote == null) {
-            remote = new RemoteArchiveBean();
-            req.getSession().setAttribute(ATTR_REMOTE, remote);
-        }
-        return remote;
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ThreadNavServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ThreadNavServlet.java
deleted file mode 100644
index abb6f852e7795ad096d0b9af268e8bf0420539c2..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/ThreadNavServlet.java
+++ /dev/null
@@ -1,157 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import net.i2p.I2PAppContext;
-import net.i2p.client.naming.PetName;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.HeaderReceiver;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.syndie.data.FilteredThreadIndex;
-import net.i2p.syndie.data.ThreadIndex;
-import net.i2p.syndie.data.ThreadNode;
-import net.i2p.syndie.sml.HTMLRenderer;
-import net.i2p.syndie.sml.SMLParser;
-
-/**
- * Export the thread nav as either RDF or XML
- *
- */
-public class ThreadNavServlet extends BaseServlet {
-    public static final String PARAM_COUNT = "count";
-    public static final String PARAM_OFFSET = "offset";
-    public static final String PARAM_FORMAT = "format";
-    
-    public static final String FORMAT_RDF = "rdf";
-    public static final String FORMAT_XML = "xml";
-    
-    protected void render(User user, HttpServletRequest req, HttpServletResponse resp, ThreadIndex index) throws ServletException, IOException {
-        int threadCount = empty(req, PARAM_COUNT) ? index.getRootCount() : getInt(req, PARAM_COUNT);
-        int offset = getInt(req, PARAM_OFFSET);
-        String uri = req.getRequestURI();
-        if (uri.endsWith(FORMAT_XML)) {
-            resp.setContentType("text/xml; charset=UTF-8");
-            render(user, index, resp.getWriter(), threadCount, offset, FORMAT_XML);
-        } else {
-            resp.setContentType("application/rdf+xml; charset=UTF-8");
-            render(user, index, resp.getWriter(), threadCount, offset, FORMAT_RDF);
-        }
-    }
-    
-    private int getInt(HttpServletRequest req, String param) {
-        String val = req.getParameter(param);
-        if (val != null) {
-            try {
-                return Integer.parseInt(val);
-            } catch (NumberFormatException nfe) {
-                // ignore
-            }
-        }
-        return -1;
-    }
-    
-    private static final int DEFAULT_THREADCOUNT = 10;
-    private static final int DEFAULT_THREADOFFSET = 0;
-    
-    private void render(User user, ThreadIndex index, PrintWriter out, int threadCount, int offset, String format) throws IOException {
-        int startRoot = DEFAULT_THREADOFFSET;
-        if (offset >= 0)
-            startRoot = offset;
-        renderStart(out, format);
-
-        int endRoot = startRoot + (threadCount > 0 ? threadCount : DEFAULT_THREADCOUNT);
-        if (endRoot >= index.getRootCount())
-            endRoot = index.getRootCount() - 1;
-        for (int i = startRoot; i <= endRoot; i++) {
-            ThreadNode node = index.getRoot(i);
-            if (FORMAT_XML.equals(format))
-                out.write(node.toString());
-            else
-                render(user, node, out);
-        }
-        renderEnd(out, format);
-    }
-    private void renderStart(PrintWriter out, String format) throws IOException {
-        out.write("<?xml version=\"1.0\" ?>\n");
-        if (FORMAT_XML.equals(format)) {
-            out.write("<threadTree>");
-        } else {
-            out.write("<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" " +
-                      "         xmlns:syndie=\"http://syndie.i2p.net/syndie.ns#\">\n");
-            out.write("<rdf:Seq rdf:about=\"http://syndie.i2p.net/threads\">\n");
-        }
-    }
-    private void renderEnd(PrintWriter out, String format) throws IOException {
-        if (FORMAT_XML.equals(format)) {
-            out.write("</threadTree>");
-        } else {
-            out.write("</rdf:Seq>\n");
-            out.write("</rdf:RDF>\n");
-        }
-    }
-    private void render(User user, ThreadNode node, PrintWriter out) throws IOException {
-        Archive archive = BlogManager.instance().getArchive();
-        String blog = node.getEntry().getKeyHash().toBase64();
-        out.write("<rdf:li rdf:resource=\"entry://" + blog + "/" + node.getEntry().getEntryId() + "\">\n");
-        out.write("<rdf:Description rdf:about=\"entry://" + blog + "/" + node.getEntry().getEntryId() + "\">");
-        PetName pn = user.getPetNameDB().getByLocation(blog);
-        String name = null;
-        if (pn != null) {
-            if (pn.isMember(FilteredThreadIndex.GROUP_FAVORITE))
-                out.write("<syndie:favoriteauthor />\n");
-            if (pn.isMember(FilteredThreadIndex.GROUP_IGNORE))
-                out.write("<syndie:ignoredauthor />\n");
-            name = pn.getName();
-        } else {
-            BlogInfo info = archive.getBlogInfo(node.getEntry().getKeyHash());
-            if (info != null)
-                name = info.getProperty(BlogInfo.NAME);
-            if ( (name == null) || (name.trim().length() <= 0) )
-                name = node.getEntry().getKeyHash().toBase64().substring(0,6);
-        }
-        out.write("<syndie:author syndie:blog=\"" + blog + "\">" + HTMLRenderer.sanitizeStrippedXML(name) + "</syndie:author>\n");
-        if ( (user.getBlog() != null) && (node.containsAuthor(user.getBlog())) )
-            out.write("<syndie:threadself />\n");
-        
-        EntryContainer entry = archive.getEntry(node.getEntry());
-        if (entry == null) throw new RuntimeException("Unable to fetch the entry " + node.getEntry());
-
-        SMLParser parser = new SMLParser(I2PAppContext.getGlobalContext());
-        HeaderReceiver rec = new HeaderReceiver();
-        parser.parse(entry.getEntry().getText(), rec);
-        String subject = rec.getHeader(HTMLRenderer.HEADER_SUBJECT);
-        if ( (subject == null) || (subject.trim().length() <= 0) )
-            subject = "(no subject)";
-        
-        out.write("<syndie:subject>" + HTMLRenderer.sanitizeStrippedXML(subject) + "</syndie:subject>\n");
-        
-        long dayBegin = BlogManager.instance().getDayBegin();
-        long postId = node.getEntry().getEntryId();
-        int daysAgo = (int)((dayBegin - postId + 24*60*60*1000l-1l)/(24*60*60*1000l));
-        out.write("<syndie:age>" + daysAgo + "</syndie:age>\n");
-        
-        out.write("<syndie:children>");
-        out.write("<rdf:Seq rdf:about=\"entry://" + blog + "/" + node.getEntry().getEntryId() + "\">");
-        for (int i = 0; i < node.getChildCount(); i++)
-            render(user, node.getChild(i), out);
-        out.write("</rdf:Seq>\n");
-        out.write("</syndie:children>\n");
-        
-        out.write("</rdf:Description>\n");
-        out.write("</rdf:li>\n");
-    }
-    
-    protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, 
-                                        int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
-        throw new UnsupportedOperationException("Not relevant...");
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ViewBlogServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ViewBlogServlet.java
deleted file mode 100644
index 6a254f52145b2ece81861429d58802b4a8ad7e1e..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/ViewBlogServlet.java
+++ /dev/null
@@ -1,819 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import net.i2p.client.naming.PetName;
-import net.i2p.data.Base64;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.ArchiveIndex;
-import net.i2p.syndie.data.Attachment;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogInfoData;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.syndie.data.FilteredThreadIndex;
-import net.i2p.syndie.data.ThreadIndex;
-import net.i2p.syndie.data.ThreadNode;
-import net.i2p.syndie.sml.BlogPostInfoRenderer;
-import net.i2p.syndie.sml.BlogRenderer;
-import net.i2p.syndie.sml.HTMLRenderer;
-import net.i2p.syndie.sml.ThreadedHTMLRenderer;
-import net.i2p.util.FileUtil;
-import net.i2p.util.Log;
-
-/**
- * Render the appropriate posts for the current blog, using any blog info data available    
- *
- */
-public class ViewBlogServlet extends BaseServlet {    
-    public static final String PARAM_OFFSET = "offset";
-    /** $blogHash */
-    public static final String PARAM_BLOG = "blog";
-    /** $blogHash/$entryId */
-    public static final String PARAM_ENTRY = "entry";
-    /** tag,tag,tag */
-    public static final String PARAM_TAG = "tag";
-    /** $blogHash/$entryId/$attachmentId */
-    public static final String PARAM_ATTACHMENT = "attachment";
-    /** image within the BlogInfoData to load (e.g. logo.png, icon_$tagHash.png, etc) */
-    public static final String PARAM_IMAGE = "image";
-    
-    public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        req.setCharacterEncoding("UTF-8");
-        String attachment = req.getParameter(PARAM_ATTACHMENT);
-        if (attachment != null) {
-            // if they requested an attachment, serve it up to 'em
-            if (renderAttachment(req, resp, attachment))
-                return;
-        }
-        String img = req.getParameter(PARAM_IMAGE);
-        if (img != null) {
-            boolean rendered = renderUpdatedImage(img, req, resp);
-            if (!rendered)
-                rendered = renderPublishedImage(img, req, resp);
-            if (!rendered)
-                rendered = renderDefaultImage(img, req, resp);
-            if (rendered) return;
-        }
-        super.service(req, resp);
-    }
-    
-    protected void render(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws ServletException, IOException {
-        Archive archive = BlogManager.instance().getArchive();
-
-        Hash blog = null;
-        String name = req.getParameter(PARAM_BLOG);
-        if ( (name == null) || (name.trim().length() <= 0) ) {
-            blog = user.getBlog();
-        } else {
-            byte val[] = Base64.decode(name);
-            if ( (val != null) && (val.length == Hash.HASH_LENGTH) )
-                blog = new Hash(val);
-        }
-        
-        BlogInfo info = null;
-        if (blog != null)
-            info = archive.getBlogInfo(blog);
-        
-        int offset = 0;
-        String off = req.getParameter(PARAM_OFFSET);
-        if (off != null) try { offset = Integer.parseInt(off); } catch (NumberFormatException nfe) {}
-
-        List posts = getPosts(user, archive, info, req, index);
-        render(user, req, out, archive, info, posts, offset);
-    }
-    
-    private BlogURI getEntry(HttpServletRequest req) {
-        String param = req.getParameter(PARAM_ENTRY);
-        if (param != null)
-            return new BlogURI("blog://" + param);
-        return null;
-    }
-    
-    private List getPosts(User user, Archive archive, BlogInfo info, HttpServletRequest req, ThreadIndex index) {
-        List rv = new ArrayList(1);
-        if (info == null) return rv;
-    
-        String entrySelected = req.getParameter(PARAM_ENTRY);
-        if (entrySelected != null) {
-            // $blogKey/$entryId
-            BlogURI uri = null;
-            if (entrySelected.startsWith("blog://"))
-                uri = new BlogURI(entrySelected);
-            else
-                uri = new BlogURI("blog://" + entrySelected.trim());
-            if (uri.getEntryId() >= 0) {
-                rv.add(uri);
-                return rv;
-            }
-        }
-        
-        ArchiveIndex aindex = archive.getIndex();
-        
-        BlogURI uri = getEntry(req);
-        if (uri != null) {
-            rv.add(uri);
-            return rv;
-        }
-        
-        aindex.selectMatchesOrderByEntryId(rv, info.getKey().calculateHash(), null);
-
-        // lets filter out any posts that are not roots
-        for (int i = 0; i < rv.size(); i++) {
-            BlogURI curURI = (BlogURI)rv.get(i);
-            ThreadNode node = index.getNode(curURI);
-            if ( (node != null) && (node.getParent() == null) ) {
-                // ok, its a root
-                Collection tags = node.getTags();
-                if ( (tags != null) && (tags.contains(BlogInfoData.TAG)) ) {
-                    // skip this, as its an info post
-                    rv.remove(i);
-                    i--;
-                }
-            } else {
-                rv.remove(i);
-                i--;
-            }
-        }
-        return rv;
-    }
-    
-    private void render(User user, HttpServletRequest req, PrintWriter out, Archive archive, BlogInfo info, List posts, int offset) throws IOException {
-        String title = null;
-        String desc = null;
-        BlogInfoData data = null;
-        if (info != null) {
-            title = info.getProperty(BlogInfo.NAME);
-            desc = info.getProperty(BlogInfo.DESCRIPTION);
-            String dataURI = info.getProperty(BlogInfo.SUMMARY_ENTRY_ID);
-            if (dataURI != null) {
-                EntryContainer entry = archive.getEntry(new BlogURI(dataURI));
-                if (entry != null) {
-                    data = new BlogInfoData();
-                    try {
-                        data.load(entry);
-                    } catch (IOException ioe) {
-                        data = null;
-                        if (_log.shouldLog(Log.WARN))
-                            _log.warn("Error loading the blog info data from " + dataURI, ioe);
-                    }
-                }
-            }
-        }
-        String pageTitle = "Syndie :: Blogs" + (desc != null ? " :: " + desc : "");
-        if (title != null) pageTitle = pageTitle + " (" + title + ")";
-        pageTitle = HTMLRenderer.sanitizeString(pageTitle);
-        out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
-        out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
-        out.write("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n<head>\n<title>" + pageTitle + "</title>\n");
-        if (info != null)
-            out.write("<link href=\"rss.jsp?" + ThreadedHTMLRenderer.PARAM_AUTHOR + "=" 
-                      + info.getKey().calculateHash().toBase64() 
-                      + "\" rel=\"alternate\" type=\"application/rss+xml\" >\n");
-        out.write("<style>");
-        renderStyle(out, info, data, req);
-        out.write("</style></head>");
-        renderHeader(user, req, out, info, data, title, desc);
-        renderReferences(user, out, info, data, req, posts);
-        renderBody(user, out, info, data, posts, offset, archive, req);
-        out.write("</body></html>\n");
-    }
-    private void renderStyle(PrintWriter out, BlogInfo info, BlogInfoData data, HttpServletRequest req) throws IOException {
-        // modify it based on data.getStyleOverrides()...
-        out.write(CSS);
-        Reader css = null;
-        try {
-            InputStream in = req.getSession().getServletContext().getResourceAsStream("/syndie.css");
-            if (in != null) {
-                css = new InputStreamReader(in, "UTF-8");
-                char buf[] = new char[1024];
-                int read = 0;
-                while ( (read = css.read(buf)) != -1) 
-                    out.write(buf, 0, read);
-            }
-        } finally {
-            if (css != null)
-                css.close();
-        }
-        String content = FileUtil.readTextFile("./docs/syndie_standard.css", -1, true); 
-        if (content != null) out.write(content);
-    }
-    
-    public static String getLogoURL(Hash blog) {
-        if (blog == null) return "";
-        return "blog.jsp?" + PARAM_BLOG + "=" + blog.toBase64() + "&amp;" 
-               + PARAM_IMAGE + "=" + BlogInfoData.ATTACHMENT_LOGO;
-    }
-    
-    private void renderHeader(User user, HttpServletRequest req, PrintWriter out, BlogInfo info, BlogInfoData data, String title, String desc) throws IOException {
-        out.write("<body class=\"syndieBlog\">\n<span style=\"display: none\">" +
-                  "<a href=\"#content\" title=\"Skip to the blog content\">Content</a></span>\n");
-        renderNavBar(user, req, out);
-        out.write("<div class=\"syndieBlogHeader\">\n");
-        Hash kh = null;
-        if ( (info != null) && (info.getKey() != null) )
-            kh = info.getKey().calculateHash();
-        out.write("<img class=\"syndieBlogLogo\" src=\"" + getLogoURL(kh) + "\" alt=\"\" />\n");
-        String name = desc;
-        if ( (name == null) || (name.trim().length() <= 0) )
-            name = title;
-        if ( ( (name == null) || (name.trim().length() <= 0) ) && (info != null) && (kh != null) )
-            name = kh.toBase64();
-        if (name != null) {
-            String url = "blog.jsp?" + (info != null ? PARAM_BLOG + "=" + info.getKey().calculateHash().toBase64() : "");
-            out.write("<b><a href=\"" + url + "\" title=\"Go to the blog root\">" 
-                      + HTMLRenderer.sanitizeString(name) + "</a></b>");
-            out.write("<br /><a href=\"profile.jsp?" + ThreadedHTMLRenderer.PARAM_AUTHOR + "=" + info.getKey().calculateHash().toBase64() 
-                      + "\" title=\"View their profile\">profile</a> <a href=\"threads.jsp?" + ThreadedHTMLRenderer.PARAM_AUTHOR + "=" 
-                      + info.getKey().calculateHash().toBase64() + "\" title=\"View threads they have participated in\">threads</a>");
-        }
-        out.write("</div>\n");
-    }
-    
-    public static final String DEFAULT_GROUP_NAME = "References";
-    private void renderReferences(User user, PrintWriter out, BlogInfo info, BlogInfoData data, HttpServletRequest req, List posts) throws IOException {
-        out.write("<div class=\"syndieBlogLinks\">\n");
-        if (data != null) {
-            for (int i = 0; i < data.getReferenceGroupCount(); i++) {
-                List group = data.getReferenceGroup(i);
-                if (group.size() <= 0) continue;
-                PetName pn = (PetName)group.get(0);
-                String name = null;
-                if (pn.getGroupCount() <= 0)
-                    name = DEFAULT_GROUP_NAME;
-                else
-                    name = HTMLRenderer.sanitizeString(pn.getGroup(0));
-                out.write("<!-- group " + name + " -->\n");
-                out.write("<div class=\"syndieBlogLinkGroup\">\n");
-                out.write("<span class=\"syndieBlogLinkGroupName\">" + name + "</span>\n");
-                out.write("<ul>\n");
-                for (int j = 0; j < group.size(); j++) {
-                    pn = (PetName)group.get(j);
-                    out.write("<li>" + renderLink(info.getKey().calculateHash(), pn) + "</li>\n");
-                }
-                out.write("</ul>\n</div>\n<!-- end " + name + " -->\n");
-            }
-        }
-        //out.write("<div class=\"syndieBlogLinkGroup\">\n");
-        //out.write("<span class=\"syndieBlogLinkGroupName\">Custom links</span>\n");
-        //out.write("<ul><li><a href=\"\">are not yet implemented</a></li><li><a href=\"\">but are coming soon</a></li></ul>\n");
-        //out.write("</div><!-- end fake group -->");
-        
-        renderPostReferences(user, req, out, posts);
-        
-        out.write("<div class=\"syndieBlogMeta\">");
-        out.write("Secured by <a href=\"http://syndie.i2p.net/\">Syndie</a>");
-        out.write("</div>\n");
-        out.write("</div><!-- end syndieBlogLinks -->\n\n");
-    }
-    
-    private void renderPostReferences(User user, HttpServletRequest req, PrintWriter out, List posts) throws IOException {
-        if (!empty(req, PARAM_ENTRY) && (posts.size() == 1)) {
-            BlogURI uri = (BlogURI)posts.get(0);
-            Archive archive = BlogManager.instance().getArchive();
-            EntryContainer entry = archive.getEntry(uri);
-            if (entry != null) {
-                out.write("<div class=\"syndieBlogPostInfo\"><!-- foo -->\n");
-
-                BlogPostInfoRenderer renderer = new BlogPostInfoRenderer(_context);
-                renderer.render(user, archive, entry, out);
-                
-                out.write("</div><!-- end syndieBlogPostInfo -->\n");   
-            }
-        }
-    }
-    
-    /** generate a link for the given petname within the scope of the given blog */
-    public static String renderLink(Hash blogFrom, PetName pn) {
-        StringBuffer buf = new StringBuffer(64);
-        String type = pn.getProtocol();
-        if ("syndieblog".equals(type)) {
-            String loc = pn.getLocation();
-            if (loc != null) {
-                buf.append("<a href=\"blog.jsp?").append(PARAM_BLOG).append("=");
-                buf.append(HTMLRenderer.sanitizeTagParam(pn.getLocation()));
-                buf.append("\" title=\"View ").append(HTMLRenderer.sanitizeTagParam(pn.getName())).append("\">");
-            }
-            buf.append(HTMLRenderer.sanitizeString(pn.getName()));
-            if (loc != null) {
-                buf.append("</a>");
-                //buf.append(" <a href=\"").append(HTMLRenderer.getBookmarkURL(pn.getName(), pn.getLocation(), "syndie", "syndieblog"));
-                //buf.append("\" title=\"Bookmark ").append(HTMLRenderer.sanitizeTagParam(pn.getName())).append("\"><image src=\"images/addToFavorites.png\" alt=\"\" /></a>\n");
-            }
-        } else if ("syndieblogpost".equals(type)) {
-            String loc = pn.getLocation();
-            if (loc != null) {
-                buf.append("<a href=\"blog.jsp?").append(PARAM_BLOG).append("=");
-                buf.append(blogFrom.toBase64()).append("&amp;");
-                buf.append(PARAM_ENTRY).append("=").append(HTMLRenderer.sanitizeTagParam(pn.getLocation()));
-                buf.append("\" title=\"View the specified post\">");
-            }
-            buf.append(HTMLRenderer.sanitizeString(pn.getName()));
-            if (loc != null) {
-                buf.append("</a>");
-            }
-        } else if ("syndieblogattachment".equals(type)) {
-            String loc = pn.getLocation();
-            if (loc != null) {
-                int split = loc.lastIndexOf('/');
-                try {
-                    int attachmentId = -1;
-                    if (split > 0)
-                        attachmentId = Integer.parseInt(loc.substring(split+1));
-                    
-                    if (attachmentId < 0) {
-                        loc = null;
-                    } else {
-                        BlogURI post = null;
-                        if (loc.startsWith("blog://"))
-                            post = new BlogURI(loc.substring(0, split));
-                        else
-                            post = new BlogURI("blog://" + loc.substring(0, split));
-
-                        EntryContainer entry = BlogManager.instance().getArchive().getEntry(post);
-                        if (entry != null) {
-                            Attachment attachments[] = entry.getAttachments();
-                            if (attachmentId < attachments.length) {
-                                buf.append("<a href=\"blog.jsp?").append(PARAM_BLOG).append("=");
-                                buf.append(blogFrom.toBase64()).append("&amp;");
-                                buf.append(PARAM_ATTACHMENT).append("=").append(HTMLRenderer.sanitizeTagParam(loc));
-                                buf.append("\" title=\"");
-                                buf.append("'");
-                                buf.append(HTMLRenderer.sanitizeTagParam(attachments[attachmentId].getName()));
-                                buf.append("', ");
-                                buf.append(attachments[attachmentId].getDataLength()/1024).append("KB, ");
-                                buf.append("of type ").append(HTMLRenderer.sanitizeTagParam(attachments[attachmentId].getMimeType()));
-                                buf.append("\">");
-                                buf.append(HTMLRenderer.sanitizeString(pn.getName()));
-                                buf.append("</a>");
-                            } else {
-                                loc = null;
-                            }
-                        } else {
-                            loc = null;
-                        }
-                    }
-                } catch (Exception e) {
-                    e.printStackTrace();
-                    loc = null;
-                }
-            }
-            if (loc == null)
-                buf.append(HTMLRenderer.sanitizeString(pn.getName()));
-        } else if ( ("eepsite".equals(type)) || ("i2p".equals(type)) ||
-                   ("website".equals(type)) || ("http".equals(type)) || ("web".equals(type)) ) {
-            String loc = pn.getLocation();
-            if (loc != null) {
-                buf.append("<a href=\"externallink.jsp?");
-                if (pn.getNetwork() != null)
-                    buf.append("schema=").append(Base64.encode(pn.getNetwork())).append("&amp;");
-                if (pn.getLocation() != null)
-                    buf.append("location=").append(Base64.encode(pn.getLocation())).append("&amp;");
-                buf.append("\" title=\"View ").append(HTMLRenderer.sanitizeTagParam(pn.getLocation())).append("\">");
-            }
-            buf.append(HTMLRenderer.sanitizeString(pn.getName()));
-            if (loc != null) {
-                buf.append("</a>");
-            }
-        } else {
-            buf.append("<a href=\"\" title=\"go somewhere? ").append(HTMLRenderer.sanitizeString(pn.toString())).append("\">");
-            buf.append(HTMLRenderer.sanitizeString(pn.getName())).append("</a>");
-        }
-        return buf.toString();
-    }
-
-    private static final int POSTS_PER_PAGE = 5;
-    private void renderBody(User user, PrintWriter out, BlogInfo info, BlogInfoData data, List posts, int offset, Archive archive, HttpServletRequest req) throws IOException {
-        out.write("<div class=\"syndieBlogBody\">\n<span style=\"display: none\" id=\"content\"></span>\n\n");
-        if (info == null) {
-            out.write("No blog specified\n");
-            return;
-        }
-        
-        BlogRenderer renderer = new BlogRenderer(_context, info, data);
-        
-        if ( (posts.size() == 1) && (req.getParameter(PARAM_ENTRY) != null) ) {
-            BlogURI uri = (BlogURI)posts.get(0);
-            EntryContainer entry = archive.getEntry(uri);
-            renderer.renderPost(user, archive, entry, out, false, true);
-            renderComments(user, out, info, data, entry, archive, renderer);
-        } else {
-            for (int i = offset; i < posts.size() && i < offset + POSTS_PER_PAGE; i++) {
-                BlogURI uri = (BlogURI)posts.get(i);
-                EntryContainer entry = archive.getEntry(uri);
-                renderer.renderPost(user, archive, entry, out, true, true);
-            }
-
-            renderNav(out, info, data, posts, offset, archive, req);
-        }
-
-        out.write("</div><!-- end syndieBlogBody -->\n");
-    }
-    
-    private void renderComments(User user, PrintWriter out, BlogInfo info, BlogInfoData data, EntryContainer entry, 
-                                Archive archive, BlogRenderer renderer) throws IOException {
-        ArchiveIndex index = archive.getIndex();
-        out.write("<div class=\"syndieBlogComments\">\n");
-        renderComments(user, out, entry.getURI(), archive, index, renderer);
-        out.write("</div>\n");
-    }
-    private void renderComments(User user, PrintWriter out, BlogURI parentURI, Archive archive, ArchiveIndex index, BlogRenderer renderer) throws IOException {
-        List replies = index.getReplies(parentURI);
-        if (replies.size() > 0) {
-            out.write("<ul>\n");
-            for (int i = 0; i < replies.size(); i++) {
-                BlogURI uri = (BlogURI)replies.get(i);
-                out.write("<li>");
-                if (!shouldIgnore(user, uri)) {
-                    EntryContainer cur = archive.getEntry(uri);
-                    renderer.renderComment(user, archive, cur, out);
-                    // recurse
-                    renderComments(user, out, uri, archive, index, renderer);
-                }
-                out.write("</li>\n");
-            }
-            out.write("</ul>\n");
-        }
-    }
-    
-    private boolean shouldIgnore(User user, BlogURI uri) {
-        PetName pn = user.getPetNameDB().getByLocation(uri.getKeyHash().toBase64());
-        return ( (pn != null) && pn.isMember(FilteredThreadIndex.GROUP_IGNORE));
-    }
-    
-    private void renderNav(PrintWriter out, BlogInfo info, BlogInfoData data, List posts, int offset, Archive archive, HttpServletRequest req) throws IOException {
-        out.write("<div class=\"syndieBlogNav\"><hr style=\"display: none\" />\n");
-        String uri = req.getRequestURI() + "?";
-        if (info != null)
-            uri = uri + PARAM_BLOG + "=" + info.getKey().calculateHash().toBase64() + "&amp;";
-        if (offset + POSTS_PER_PAGE >= posts.size())
-            out.write(POSTS_PER_PAGE + " more older entries");
-        else
-            out.write("<a href=\"" + uri + "offset=" + (offset+POSTS_PER_PAGE) + "\">" 
-                      + POSTS_PER_PAGE + " older entries</a>");
-        out.write(" | ");
-        if (offset <= 0)
-            out.write(POSTS_PER_PAGE + " more recent entries");
-        else
-            out.write("<a href=\"" + uri + "offset=" + 
-                      (offset >= POSTS_PER_PAGE ? offset-POSTS_PER_PAGE : 0) 
-                      + "\">" + POSTS_PER_PAGE + " more recent entries</a>");
-        
-        out.write("</div><!-- end syndieBlogNav -->\n");
-    }
-
-    /** 
-     * render the attachment to the browser, using the appropriate mime types, etc
-     * @param attachment formatted as $blogHash/$entryId/$attachmentId
-     * @return true if rendered 
-     */
-    private boolean renderAttachment(HttpServletRequest req, HttpServletResponse resp, String attachment) throws ServletException, IOException {
-        int split = attachment.lastIndexOf('/');
-        if (split <= 0)
-            return false;
-        BlogURI uri = new BlogURI("blog://" + attachment.substring(0, split));
-        try { 
-            int attachmentId = Integer.parseInt(attachment.substring(split+1)); 
-            if (attachmentId < 0) return false;
-            EntryContainer entry = BlogManager.instance().getArchive().getEntry(uri);
-            if (entry == null) {
-                System.out.println("Could not render the attachment [" + uri + "] / " + attachmentId);
-                return false;
-            }
-            Attachment attachments[] = entry.getAttachments();
-            if (attachmentId >= attachments.length) {
-                System.out.println("Out of range attachment on " + uri + ": " + attachmentId);
-                return false;
-            }
-            
-            resp.setContentType(ArchiveViewerBean.getAttachmentContentType(attachments[attachmentId]));
-            boolean inline = ArchiveViewerBean.getAttachmentShouldShowInline(attachments[attachmentId]);
-            String filename = ArchiveViewerBean.getAttachmentFilename(attachments[attachmentId]);
-            if (inline)
-                resp.setHeader("Content-Disposition", "inline; filename=\"" + filename + "\"");
-            else
-                resp.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
-            int len = ArchiveViewerBean.getAttachmentContentLength(attachments[attachmentId]);
-            if (len >= 0)
-                resp.setContentLength(len);
-            ArchiveViewerBean.renderAttachment(attachments[attachmentId], resp.getOutputStream());
-            return true;
-        } catch (NumberFormatException nfe) {}
-        return false;
-    }
-
-    
-    private boolean renderUpdatedImage(String requestedImage, HttpServletRequest req, HttpServletResponse resp) throws IOException {
-        BlogConfigBean bean = BlogConfigServlet.getConfigBean(req);
-        if ( (bean != null) && (bean.isUpdated()) && (bean.getLogo() != null) ) {
-            // the updated image only affects *our* blog...
-            User u = bean.getUser();
-            if (u != null) {
-                String reqBlog = req.getParameter(PARAM_BLOG);
-                if ( (reqBlog == null) || (u.getBlog().toBase64().equals(reqBlog)) ) {
-                    if (BlogInfoData.ATTACHMENT_LOGO.equals(requestedImage)) {
-                        File logo = bean.getLogo();
-                        if (logo != null) {
-                            byte buf[] = new byte[4096];
-                            resp.setContentType("image/png");
-                            resp.setContentLength((int)logo.length());
-                            OutputStream out = resp.getOutputStream();
-                            FileInputStream in = null;
-                            try {
-                                in = new FileInputStream(logo);
-                                int read = 0;
-                                while ( (read = in.read(buf)) != -1) 
-                                    out.write(buf, 0, read);
-                                _log.debug("Done writing the updated full length logo");
-                            } finally {
-                                if (in != null) try { in.close(); } catch (IOException ioe) {}
-                                if (out != null) try { out.close(); } catch (IOException ioe) {}
-                            }
-                            _log.debug("Returning from writing the updated full length logo");
-                            return true;
-                        }
-                    } else {
-                        // ok, the blogConfigBean doesn't let people configure other things yet... fall through
-                    }
-                }
-            }
-        }
-        return false;
-    }
-    
-    private boolean renderPublishedImage(String requestedImage, HttpServletRequest req, HttpServletResponse resp) throws IOException {
-        // nothing matched in the updated config, lets look at the current published info
-        String blog = req.getParameter(PARAM_BLOG);
-        if (blog != null) {
-            Archive archive = BlogManager.instance().getArchive();
-            byte h[] = Base64.decode(blog);
-            if ( (h != null) && (h.length == Hash.HASH_LENGTH) ) {
-                Hash blogHash = new Hash(h);
-                BlogInfo info = archive.getBlogInfo(blogHash);
-                String entryId = info.getProperty(BlogInfo.SUMMARY_ENTRY_ID);
-                _log.debug("Author's entryId: " + entryId);
-                if (entryId != null) {
-                    BlogURI dataURI = new BlogURI(entryId);
-                    EntryContainer entry = archive.getEntry(dataURI);
-                    if (entry != null) {
-                        BlogInfoData data = new BlogInfoData();
-                        try {
-                            data.load(entry);
-
-                            _log.debug("Blog info data loaded from: " + entryId);
-                            Attachment toWrite = null;
-                            if (BlogInfoData.ATTACHMENT_LOGO.equals(requestedImage)) {
-                                toWrite = data.getLogo();
-                            } else {
-                                toWrite = data.getOtherAttachment(requestedImage);
-                            }
-                            if (toWrite != null) {
-                                resp.setContentType("image/png");
-                                resp.setContentLength(toWrite.getDataLength());
-                                InputStream in = null;
-                                OutputStream out = null;
-                                try {
-                                    in = toWrite.getDataStream();
-                                    out = resp.getOutputStream();
-                                    byte buf[] = new byte[4096];
-                                    int read = -1;
-                                    while ( (read = in.read(buf)) != -1)
-                                        out.write(buf, 0, read);
-                                    
-                                    _log.debug("Write image from: " + entryId);
-                                    return true;
-                                } finally { 
-                                    if (in != null) try { in.close(); } catch (IOException ioe) {}
-                                    if (out != null) try { out.close(); } catch (IOException ioe) {}
-                                }
-                            }
-                        } catch (IOException ioe) {
-                            _log.debug("Error reading/writing: " + entryId, ioe);
-                            data = null;
-                        }
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    /** 1px png, base64 encoded, used if they asked for an image that we dont know of */
-    private static final byte BLANK_IMAGE[] = Base64.decode("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQI12NgYAAAAAMAASDVlMcAAAAASUVORK5CYII=");    
-    private boolean renderDefaultImage(String requestedImage, HttpServletRequest req, HttpServletResponse resp) throws IOException {
-        if (requestedImage.equals("logo.png")) {
-            InputStream in = req.getSession().getServletContext().getResourceAsStream("/images/default_blog_logo.png");
-            if (in != null) {
-                resp.setContentType("image/png");
-                OutputStream out = resp.getOutputStream();
-                try {
-                    byte buf[] = new byte[4096];
-                    int read = -1;
-                    while ( (read = in.read(buf)) != -1) 
-                        out.write(buf, 0, read);
-                    _log.debug("Done writing default logo");
-                } finally {
-                    try { in.close(); } catch (IOException ioe) {}
-                    try { out.close(); } catch (IOException ioe) {}
-                }
-                return true;
-            }
-        }
-        resp.setContentType("img.png");
-        resp.setContentLength(BLANK_IMAGE.length);
-        OutputStream out = resp.getOutputStream();
-        try {
-            out.write(BLANK_IMAGE);
-        } finally {
-            try { out.close(); } catch (IOException ioe) {}
-        }
-        _log.debug("Done writing default image");
-        return true;
-    }
-    
-    private static final String CSS = "\n" +
-"* {\n" +
-"    margin: 0px;\n" +
-"    padding: 0px;\n" +
-"}\n" +
-"body {\n" +
-"    font-family: Arial, Helvetica, sans-serif;\n" +
-"    font-size: 100%;\n" +
-"    background-color : #EEEEEE;\n" +
-"}\n" +
-"a {\n" +
-"    text-decoration: none;\n" +
-"}\n" +
-"a:hover {\n" +
-"    color: red;\n" +
-"}\n" +
-"select {\n" +
-"    min-width: 1.5em;\n" +
-"}\n" +
-".syndieBlog {\n" +
-"}\n" +
-".syndieBlogTopNav {\n" +
-"    float:left;\n" +
-"    width: 100%;\n" +
-"    background-color: #BBBBBB;\n" +
-"}\n" +
-".syndieBlogTopNavUser {\n" +
-"    text-align: left;\n" +
-"    float: left;\n" +
-"    margin: 2px;\n" +
-"}\n" +
-".syndieBlogTopNavAdmin {\n" +
-"    text-align: left;\n" +
-"    float: right;\n" +
-"    margin: 2px;\n" +
-"}\n" +
-".syndieBlogHeader {\n" +
-"    width: 100%;\n" +
-"    background-color: black;\n" +
-"    float:left;\n" +
-"}\n" +
-".syndieBlogHeader a {\n" +
-"    color: white;\n" +
-"    padding: 4px;\n" +
-"}\n" +
-".syndieBlogHeader b {\n" +
-"    font-size: 1.2em;\n" +
-"}\n" +
-".syndieBlogLogo {\n" +
-"    float: left;\n" +
-"}\n" +
-".syndieBlogLinks {\n" +
-"    width: 20%;\n" +
-"    float: left;\n" +
-"}\n" +
-".syndieBlogLinkGroup {\n" +
-"    font-size: 0.8em;\n" +
-"    background-color: #DDD;\n" +
-"    border: 1px solid black;\n" +
-"    margin: 5px;\n" +
-"    padding: 2px;\n" +
-"}\n" +
-".syndieBlogLinkGroup ul {\n" +
-"    list-style: none;\n" +
-"}\n" +
-".syndieBlogLinkGroup li {\n" +
-"    width: 100%;\n" +
-"    overflow: hidden;\n" +
-"    white-space: nowrap;\n" +
-"}\n" +
-".syndieBlogLinkGroupName {\n" +
-"    font-weight: bold;\n" +
-"    width: 100%;\n" +
-"    border-bottom: 1px dashed black;\n" +
-"    display: block;\n" +
-"    overflow: hidden;\n" +
-"    white-space: nowrap;\n" +
-"}\n" +
-".syndieBlogPostInfoGroup {\n" +
-"    font-size: 0.8em;\n" +
-"    background-color: #FFEA9F;\n" +
-"    border: 1px solid black;\n" +
-"    margin: 5px;\n" +
-"    padding: 2px;\n" +
-"}\n" +
-".syndieBlogPostInfoGroup ol {\n" +
-"    list-style: none;\n" +
-"}\n" +
-".syndieBlogPostInfoGroup li {\n" +
-"    white-space: nowrap;\n" +
-"    width: 100%;\n" +
-"    overflow: hidden;\n" +
-"}\n" +
-".syndieBlogPostInfoGroupName {\n" +
-"    font-weight: bold;\n" +
-"    width: 100%;\n" +
-"    border-bottom: 1px dashed black;\n" +
-"    display: block;\n" +
-"    overflow: hidden;\n" +
-"    white-space: nowrap;\n" +
-"}\n" +
-".syndieBlogMeta {\n" +
-"    text-align: left;\n" +
-"    font-size: 0.8em;\n" +
-"    background-color: #DDD;\n" +
-"    border: 1px solid black;\n" +
-"    margin: 5px;\n" +
-"    padding: 2px;\n" +
-"}\n" +
-".syndieBlogBody {\n" +
-"    width: 80%;\n" +
-"    float: left;\n" +
-"}\n" +
-".syndieBlogPost {\n" +
-"    border: 1px solid black;\n" +
-"    margin-top: 5px;\n" +
-"    margin-right: 5px;\n" +
-"    word-wrap: break-word;\n" +
-"}\n" +
-".syndieBlogPostHeader {\n" +
-"    background-color: #BBB;\n" +
-"    padding: 2px;\n" +
-"}\n" +
-".syndieBlogPostSubject {\n" +
-"    font-weight: bold;\n" +
-"}\n" +
-".syndieBlogPostFrom {\n" +
-"    text-align: right;\n" +
-"}\n" +
-".syndieBlogPostSummary {\n" +
-"    background-color: #FFFFFF;\n" +
-"    padding: 2px;\n" +
-"}\n" +
-".syndieBlogPostDetails {\n" +
-"    background-color: #DDD;\n" +
-"    padding: 2px;\n" +
-"}\n" +
-".syndieBlogNav {\n" +
-"    text-align: center;\n" +
-"}\n" +
-".syndieBlogComments {\n" +
-"    border: none;\n" +
-"    margin-top: 5px;\n" +
-"    margin-left: 0px;\n" +
-"    float: left;\n" +
-"}\n" +
-".syndieBlogComments ul {\n" +
-"    list-style: none;\n" +
-"    margin-left: 10px;\n" +
-"}\n" +
-".syndieBlogCommentInfoGroup {\n" +
-"    font-size: 0.8em;\n" +
-"    margin-right: 5px;\n" +
-"}\n" +
-".syndieBlogCommentInfoGroup ol {\n" +
-"    list-style: none;\n" +
-"}\n" +
-".syndieBlogCommentInfoGroup li {\n" +
-"}\n" +
-".syndieBlogCommentInfoGroupName {\n" +
-"    font-size: 0.8em;\n" +
-"    font-weight: bold;\n" +
-"}\n";
-    protected String getTitle() { return "unused"; }
-    protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, 
-                                        int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
-        throw new RuntimeException("unused");
-    }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ViewBlogsServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ViewBlogsServlet.java
deleted file mode 100644
index 4a1d4d2586d42c07376807110504e2b58e730d14..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/ViewBlogsServlet.java
+++ /dev/null
@@ -1,168 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.TreeSet;
-
-import javax.servlet.http.HttpServletRequest;
-
-import net.i2p.client.naming.PetName;
-import net.i2p.client.naming.PetNameDB;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.NewestEntryFirstComparator;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.FilteredThreadIndex;
-import net.i2p.syndie.data.ThreadIndex;
-import net.i2p.syndie.data.ThreadNode;
-import net.i2p.syndie.sml.HTMLRenderer;
-
-/**
- * List the blogs known in the archive
- *
- */
-public class ViewBlogsServlet extends BaseServlet {
-    private static final int MAX_AUTHORS_AT_ONCE = 20;
-    private static final int MAX_TAGS = 50;
-    
-    /** renders the posts from the last 3 days */
-    private String getViewBlogLink(Hash blog, long lastPost) {
-        long dayBegin = BlogManager.instance().getDayBegin();
-        int daysAgo = 2;
-        if ( (lastPost > 0) && (dayBegin - 3*24*60*60*1000l >= lastPost) ) // last post was old 3 days ago
-            daysAgo = (int)((dayBegin - lastPost + 24*60*60*1000l-1)/(24*60*60*1000l));
-        daysAgo++;
-        return "blog.jsp?" + ViewBlogServlet.PARAM_BLOG + "=" + blog.toBase64();
-        //return getControlTarget() + "?" + ThreadedHTMLRenderer.PARAM_AUTHOR + '=' + blog.toBase64()
-        //       + '&' + ThreadedHTMLRenderer.PARAM_THREAD_AUTHOR + "=true&daysBack=" + daysAgo;
-    }
-    
-    private String getPostDate(long when) {
-        String age = null;
-        long dayBegin = BlogManager.instance().getDayBegin();
-        long postId = when;
-        if (postId >= dayBegin) {
-            age = "today";
-        } else if (postId >= dayBegin - 24*60*60*1000) {
-            age = "yesterday";
-        } else {
-            int daysAgo = (int)((dayBegin - postId + 24*60*60*1000-1)/(24*60*60*1000));
-            age = daysAgo + " days ago";
-        }
-        return age;
-    }
-    
-    protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, 
-                                        int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
-        TreeSet orderedRoots = new TreeSet(new NewestEntryFirstComparator());
-        // The thread index is ordered by last updated date, as opposed to root posting date,
-        // so lets reorder things
-        int count = index.getRootCount();
-        for (int i = 0; i < count; i++) {
-            ThreadNode node = index.getRoot(i);
-            orderedRoots.add(node.getEntry());
-        }
-        
-        TreeSet tags = new TreeSet();
-        List writtenAuthors = new ArrayList();
-        
-        
-        out.write("<tr><td colspan=\"3\" valign=\"top\" align=\"left\"><span class=\"syndieBlogFavorites\">");
-        if ( (user != null) && (user.getAuthenticated()) ) {
-            out.write("<b>Favorite blogs:</b> <a href=\"" + getControlTarget() + "?author=favorites&daysBack=3\" title=\"View all posts by your favorite authors in the last 3 days\">view all</a><br />\n");
-            out.write("<a href=\"" + getViewBlogLink(user.getBlog(), user.getLastMetaEntry()) 
-                      + "\" title=\"View your blog\">Your blog</a><br />\n");
-            
-            PetNameDB db = user.getPetNameDB();
-            for (Iterator iter = orderedRoots.iterator(); iter.hasNext() && writtenAuthors.size() < MAX_AUTHORS_AT_ONCE; ) {
-                BlogURI uri= (BlogURI)iter.next();
-                if (writtenAuthors.contains(uri.getKeyHash())) {
-                    // skip
-                } else {
-                    PetName pn = db.getByLocation(uri.getKeyHash().toBase64());
-                    if (pn != null) {
-                        if (pn.isMember(FilteredThreadIndex.GROUP_FAVORITE)) {
-                            out.write("<a href=\"" + getViewBlogLink(uri.getKeyHash(), uri.getEntryId()) 
-                                      + "\" title=\"View " + HTMLRenderer.sanitizeTagParam(pn.getName()) +"'s blog\">");
-                            out.write(HTMLRenderer.sanitizeString(pn.getName(), 32));
-                            out.write("</a> (" + getPostDate(uri.getEntryId()) + ")<br />\n");
-                            writtenAuthors.add(uri.getKeyHash());
-                        } else if (pn.isMember(FilteredThreadIndex.GROUP_IGNORE)) {
-                            // ignore 'em
-                            writtenAuthors.add(uri.getKeyHash());
-                        } else {
-                            // bookmarked, but not a favorite... leave them for later
-                        }
-                    } else {
-                        // not bookmarked, leave them for later
-                    }
-                }
-            }
-        }
-        out.write("</span>\n");
-    
-        // now for the non-bookmarked people
-        out.write("<span class=\"syndieBlogList\">");
-        out.write("<b>Most recently updated blogs:</b><br />\n");
-        for (Iterator iter = orderedRoots.iterator(); iter.hasNext() && writtenAuthors.size() < MAX_AUTHORS_AT_ONCE; ) {
-            BlogURI uri= (BlogURI)iter.next();
-            String curTags[] = archive.getEntry(uri).getTags();
-            if (curTags != null)
-                for (int i = 0; i < curTags.length && tags.size() < MAX_TAGS; i++)
-                    tags.add(curTags[i]);
-            if (writtenAuthors.contains(uri.getKeyHash())) {
-                // skip
-            } else {
-                BlogInfo info = archive.getBlogInfo(uri);
-                if (info == null)
-                    continue;
-                String name = info.getProperty(BlogInfo.NAME);
-                if ( (name == null) || (name.trim().length() <= 0) )
-                    name = uri.getKeyHash().toBase64().substring(0,8);
-                String desc = info.getProperty(BlogInfo.DESCRIPTION);
-                if ( (desc == null) || (desc.trim().length() <= 0) ) 
-                    desc = name + "'s blog";
-                String age = null;
-                long dayBegin = BlogManager.instance().getDayBegin();
-                long postId = uri.getEntryId();
-                if (postId >= dayBegin) {
-                    age = "today";
-                } else if (postId >= dayBegin - 24*60*60*1000) {
-                    age = "yesterday";
-                } else {
-                    int daysAgo = (int)((dayBegin - postId + 24*60*60*1000-1)/(24*60*60*1000));
-                    age = daysAgo + " days ago";
-                }
-                
-                out.write("<a href=\"" + getViewBlogLink(uri.getKeyHash(), uri.getEntryId())
-                          + "\" title=\"View " + trim(HTMLRenderer.sanitizeTagParam(name), 32)
-                          + "'s blog\">");
-                out.write(HTMLRenderer.sanitizeString(desc, 32));
-                out.write("</a> (" + getPostDate(uri.getEntryId()) + ")<br />\n");
-                writtenAuthors.add(uri.getKeyHash());
-            }
-        }
-        
-        out.write("</span>\n");
-        /*
-        out.write("<tr><td colspan=\"3\"><b>Topics:</b></td></tr>\n");
-        out.write("<tr><td colspan=\"3\">");
-        for (Iterator iter = tags.iterator(); iter.hasNext(); ) {
-            String tag = (String)iter.next();
-            out.write("<a href=\"" + ThreadedHTMLRenderer.getFilterByTagLink(getControlTarget(), null, user, tag, null) 
-                      + "\" title=\"View threads flagged with the tag '" + HTMLRenderer.sanitizeTagParam(tag) + "'\">");
-            out.write(HTMLRenderer.sanitizeString(tag, 32));
-            out.write("</a> ");
-        }
-         */
-        out.write("</td></tr>\n");
-    }
-    
-    protected String getTitle() { return "Syndie :: View blogs"; }
-}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ViewThreadedServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ViewThreadedServlet.java
deleted file mode 100644
index 065bed3775300078c7f29fb190ec83e35b26f3f2..0000000000000000000000000000000000000000
--- a/apps/syndie/java/src/net/i2p/syndie/web/ViewThreadedServlet.java
+++ /dev/null
@@ -1,478 +0,0 @@
-package net.i2p.syndie.web;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.servlet.http.HttpServletRequest;
-
-import net.i2p.I2PAppContext;
-import net.i2p.client.naming.PetName;
-import net.i2p.client.naming.PetNameDB;
-import net.i2p.data.Base64;
-import net.i2p.data.Hash;
-import net.i2p.syndie.Archive;
-import net.i2p.syndie.BlogManager;
-import net.i2p.syndie.HeaderReceiver;
-import net.i2p.syndie.User;
-import net.i2p.syndie.data.ArchiveIndex;
-import net.i2p.syndie.data.BlogInfo;
-import net.i2p.syndie.data.BlogURI;
-import net.i2p.syndie.data.EntryContainer;
-import net.i2p.syndie.data.FilteredThreadIndex;
-import net.i2p.syndie.data.ThreadIndex;
-import net.i2p.syndie.data.ThreadNode;
-import net.i2p.syndie.sml.HTMLRenderer;
-import net.i2p.syndie.sml.SMLParser;
-import net.i2p.syndie.sml.ThreadedHTMLRenderer;
-
-/**
- * Render the appropriate posts and the thread tree
- *
- */
-public class ViewThreadedServlet extends BaseServlet {
-    protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, 
-                                        int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
-        List posts = getPosts(user, archive, req, index);
-        renderBody(user, req, out, index, archive, posts);
-        
-        renderThreadNav(user, req, out, threadOffset, index);
-        renderThreadTree(user, req, out, threadOffset, visibleEntry, archive, index, posts);
-        renderThreadNav(user, req, out, threadOffset, index);
-    }
-    
-    private void renderBody(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, Archive archive, List posts) throws IOException  {
-        ThreadedHTMLRenderer renderer = new ThreadedHTMLRenderer(I2PAppContext.getGlobalContext());
-        
-        String uri = req.getRequestURI();
-        String off = req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET);
-        String tags = req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS);
-        String author = req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR);
-        
-        boolean authorOnly = Boolean.valueOf(req.getParameter(ThreadedHTMLRenderer.PARAM_THREAD_AUTHOR)).booleanValue(); 
-
-        for (int i = 0; i < posts.size(); i++) {
-            BlogURI post = (BlogURI)posts.get(i);
-            boolean inlineReply = (posts.size() == 1);
-            //if (true)
-            //    inlineReply = true;
-            renderer.render(user, out, archive, post, inlineReply, index, uri, getAuthActionFields(), off, tags, author, authorOnly);
-        }
-    }
-    
-    private List getPosts(User user, Archive archive, HttpServletRequest req, ThreadIndex index) {
-        List rv = new ArrayList(1);
-        String author = req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR);
-        String tags = req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS);
-        String post = req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST);
-        String thread = req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD);
-        boolean threadAuthorOnly = Boolean.valueOf(req.getParameter(ThreadedHTMLRenderer.PARAM_THREAD_AUTHOR) + "").booleanValue();
-        
-        long dayBegin = BlogManager.instance().getDayBegin();
-        String daysStr = req.getParameter(ThreadedHTMLRenderer.PARAM_DAYS_BACK);
-        int days = 1;
-        try {
-            if (daysStr != null)
-                days = Integer.parseInt(daysStr);
-        } catch (NumberFormatException nfe) {
-            days = 1;
-        }
-        dayBegin -= (days-1) * 24*60*60*1000l;
-
-        if ( (author != null) && empty(post) && empty(thread) ) {
-            ArchiveIndex aindex = archive.getIndex();
-            PetNameDB db = user.getPetNameDB();
-            if ("favorites".equals(author)) {
-                for (Iterator nameIter = db.getNames().iterator(); nameIter.hasNext(); ) {
-                    PetName pn = db.getByName((String)nameIter.next());
-                    if (pn.isMember(FilteredThreadIndex.GROUP_FAVORITE) && AddressesServlet.PROTO_BLOG.equals(pn.getProtocol()) ) {
-                        Hash loc = new Hash();
-                        byte key[] = Base64.decode(pn.getLocation());
-                        if ( (key != null) && (key.length == Hash.HASH_LENGTH) ) {
-                            loc.setData(key);
-                            aindex.selectMatchesOrderByEntryId(rv, loc, tags, dayBegin);
-                        }
-                    }
-                }
-                // always include ourselves...
-                aindex.selectMatchesOrderByEntryId(rv, user.getBlog(), tags, dayBegin);
-   
-                Collections.sort(rv, BlogURI.COMPARATOR);
-            } else {
-                Hash loc = new Hash();
-                byte key[] = Base64.decode(author);
-                if ( (key != null) && (key.length == Hash.HASH_LENGTH) ) {
-                    loc.setData(key);
-                    aindex.selectMatchesOrderByEntryId(rv, loc, tags, dayBegin);
-                } else {
-                }
-            }
-            
-            // how inefficient can we get?
-            if (threadAuthorOnly && (rv.size() > 0)) {
-                // lets filter out any posts that are not roots
-                for (int i = 0; i < rv.size(); i++) {
-                    BlogURI curURI = (BlogURI)rv.get(i);
-                    ThreadNode node = index.getNode(curURI);
-                    if ( (node != null) && (node.getParent() == null) ) {
-                        // ok, its a root
-                    } else {
-                        rv.remove(i);
-                        i--;
-                    }
-                }
-            }
-        }
-        
-        BlogURI uri = getAsBlogURI(post);
-        if ( (uri != null) && (uri.getEntryId() > 0) ) {
-            rv.add(uri);
-        } else {
-            uri = getAsBlogURI(thread);
-            if ( (uri != null) && (uri.getEntryId() > 0) ) {
-                ThreadNode node = index.getNode(uri);
-                if (node != null) {
-                    if (false) {
-                        // entire thread, as a depth first search
-                        while (node.getParent() != null)
-                            node = node.getParent(); // hope the structure is loopless...
-                        // depth first traversal
-                        walkTree(rv, node);
-                    } else {
-                        // only the "current" unforked thread, as suggested by cervantes.
-                        // e.g.
-                        //  a--b--c--d
-                        //   \-e--f--g
-                        //         \-h
-                        // would show "a--e--f--g" if node == {e, f, or g}, 
-                        // or "a--b--c--d" if node == {a, b, c, or d},
-                        // or "a--e--f--h" if node == h
-                        rv.add(node.getEntry());
-                        ThreadNode cur = node;
-                        while (cur.getParent() != null) {
-                            cur = cur.getParent();
-                            rv.add(0, cur.getEntry()); // parents go before children...
-                        }
-                        cur = node;
-                        while ( (cur != null) && (cur.getChildCount() > 0) ) {
-                            cur = cur.getChild(0);
-                            rv.add(cur.getEntry()); // and children after parents
-                        }
-                    }
-                } else {
-                    rv.add(uri);
-                }
-            }
-        }
-        
-        return rv;
-    }
-    
-    private void walkTree(List uris, ThreadNode node) {
-        if (node == null)
-            return;
-        if (uris.contains(node))
-            return;
-        uris.add(node.getEntry());
-        for (int i = 0; i < node.getChildCount(); i++)
-            walkTree(uris, node.getChild(i));
-    }
-    private void renderThreadNav(User user, HttpServletRequest req, PrintWriter out, int threadOffset, ThreadIndex index) throws IOException {
-        out.write("<tr class=\"threadNav\" id=\"threads\"><td colspan=\"2\" nowrap=\"true\">\n");
-        out.write("<!-- thread nav begin -->\n");
-        if (threadOffset == 0) {
-            out.write("&lt;&lt; First Page ");
-        } else {
-            out.write("<a href=\"");
-            out.write(getNavLink(req, 0));
-            out.write("\">&lt;&lt; First Page</a> ");
-        }
-        if (threadOffset > 0) {
-            out.write("<a href=\"");
-            int nxt = threadOffset - 10;
-            if (nxt < 0)
-                nxt = 0;
-            out.write(getNavLink(req, nxt));
-            out.write("\">&lt; Prev Page</a>\n");
-        } else {
-            out.write("&lt; Prev Page\n");
-        }
-        out.write("</td><td class=\"threadNavRight\" nowrap=\"true\">\n");
-        
-        out.write("<span class=\"rightOffset\">");
-        int max = index.getRootCount();
-        if (threadOffset + 10 > max) {
-            out.write("Next Page&gt; Last Page&gt;&gt;\n");
-        } else {
-            out.write("<a href=\"");
-            out.write(getNavLink(req, threadOffset + 10));
-            out.write("\">Next Page&gt;</a> <a href=\"");
-            out.write(getNavLink(req, -1));
-            out.write("\">Last Page&gt;&gt;</a>\n");
-        }
-        out.write("</span>");
-        //out.write("<!-- thread nav end -->\n");
-        out.write("</td></tr>\n");
-    }
-    
-    private void renderThreadTree(User user, HttpServletRequest req, PrintWriter out, int threadOffset, BlogURI visibleEntry, Archive archive, ThreadIndex index, List visibleURIs) throws IOException {
-        int numThreads = 10;
-        renderThreadTree(user, out, index, archive, req, threadOffset, numThreads, visibleEntry, visibleURIs);
-    }
-     
-    private void renderThreadTree(User user, PrintWriter out, ThreadIndex index, Archive archive, HttpServletRequest req,
-                                  int threadOffset, int numThreads, BlogURI visibleEntry, List visibleURIs) {
-        
-        if ( (visibleEntry != null) && (empty(req, ThreadedHTMLRenderer.PARAM_OFFSET)) ) {
-            // we want to jump to a specific thread in the nav
-            threadOffset = index.getRoot(visibleEntry);
-        }
-
-        if (threadOffset < 0)
-            threadOffset = 0;
-        out.write("<!-- threads begin -->\n");
-        if (threadOffset + numThreads > index.getRootCount())
-            numThreads = index.getRootCount() - threadOffset;
-        TreeRenderState state = new TreeRenderState(new ArrayList());
-        
-        int written = 0;
-        for (int curRoot = threadOffset; curRoot < numThreads + threadOffset; curRoot++) {
-            ThreadNode node = index.getRoot(curRoot);
-            out.write("<!-- thread begin curRoot=" + curRoot + " threadOffset=" + threadOffset + " -->\n");
-            renderThread(user, out, index, archive, req, node, 0, visibleEntry, state, visibleURIs);
-            out.write("<!-- thread end -->\n");
-            written++;
-        }
-        
-        if (written <= 0)
-            out.write("<tr class=\"threadEven\"><td colspan=\"3\">No matching threads</td></tr>\n");
-        
-        out.write("<!-- threads end -->\n");
-    }
-    
-    private boolean renderThread(User user, PrintWriter out, ThreadIndex index, Archive archive, HttpServletRequest req,
-                                 ThreadNode node, int depth, BlogURI visibleEntry, TreeRenderState state, List visibleURIs) {
-        boolean isFavorite = false;
-        boolean ignored = false;
-        boolean displayed = false;
-        
-        if ( (visibleURIs != null) && (visibleURIs.contains(node.getEntry())) )
-            displayed = true;
-        
-        HTMLRenderer rend = new HTMLRenderer(I2PAppContext.getGlobalContext());
-        SMLParser parser = new SMLParser(I2PAppContext.getGlobalContext());
-        
-        PetName pn = user.getPetNameDB().getByLocation(node.getEntry().getKeyHash().toBase64());
-        if (pn != null) {
-            if (pn.isMember(FilteredThreadIndex.GROUP_FAVORITE)) {
-                isFavorite = true;
-            }
-            if (pn.isMember(FilteredThreadIndex.GROUP_IGNORE))
-                ignored = true;
-        }
-        
-        state.incrementRowsWritten();
-        if (state.getRowsWritten() % 2 == 0)
-            out.write("<tr class=\"threadEven\">\n");
-        else
-            out.write("<tr class=\"threadOdd\">\n");
-
-        out.write("<td class=\"thread\" colspan=\"3\">");
-        out.write("<span class=\"threadInfoLeft\">");
-        //out.write("<td class=\"threadFlag\">");
-        out.write(getFlagHTML(user, node));
-        //out.write("</td>\n<td class=\"threadLeft\" colspan=\"2\">\n");
-        for (int i = 0; i < depth; i++)
-            out.write("<img src=\"images/threadIndent.png\" alt=\"\" border=\"0\" />");
-        
-        boolean showChildren = false;
-        
-        int childCount = node.getChildCount();
-        
-        if (childCount > 0) {
-            boolean allowCollapse = false;
-
-            if (visibleEntry != null) {
-                if (node.getEntry().equals(visibleEntry)) {
-                    // noop
-                } else if (node.containsEntry(visibleEntry)) {
-                    showChildren = true;
-                    allowCollapse = true;
-                }
-            } else {
-                // noop
-            }
-        
-            if (allowCollapse) {
-                out.write("<a href=\"");
-                out.write(getCollapseLink(req, node));
-                out.write("\" title=\"collapse thread\"><img border=\"0\" src=\"images/collapse.png\" alt=\"collapse\" /></a>\n");
-            } else {
-                out.write("<a href=\"");
-                out.write(getExpandLink(req, node));
-                out.write("\" title=\"expand thread\"><img border=\"0\" src=\"images/expand.png\" alt=\"expand\" /></a>\n");
-            }
-        } else {
-            out.write("<img src=\"images/noSubthread.png\" alt=\"\" border=\"0\" />\n");
-        }
-        
-        out.write("<a href=\"");
-        out.write(getProfileLink(req, node.getEntry().getKeyHash()));
-        out.write("\" title=\"View the user's profile\">");
-
-        if (displayed) out.write("<b>");
-        
-        if (pn == null) {
-            BlogInfo info = archive.getBlogInfo(node.getEntry().getKeyHash());
-            String name = null;
-            if (info != null)
-                name = info.getProperty(BlogInfo.NAME);
-            if ( (name == null) || (name.trim().length() <= 0) )
-                name = node.getEntry().getKeyHash().toBase64().substring(0,6);
-            out.write(trim(name, 30));
-        } else {
-            out.write(trim(pn.getName(), 30));
-        }
-        
-        if (displayed) out.write("</b>");
-        
-        out.write("</a>\n");
-
-        if ( (user.getBlog() != null) && (node.getEntry().getKeyHash().equals(user.getBlog())) ) {
-            out.write("<img src=\"images/self.png\" alt=\"You wrote this\" border=\"0\" />\n");
-        } else if (isFavorite) {
-            out.write("<img src=\"images/favorites.png\" alt=\"favorites\" border=\"0\" />\n");
-        } else if (ignored) {
-            out.write("<img src=\"images/addToIgnored.png\" alt=\"ignored\" border=\"0\" />\n");
-        } else {
-            if (user.getAuthenticated()) {
-                // give them a link to bookmark or ignore the peer
-                out.write("(<a href=\"");
-                out.write(getAddToGroupLink(req, node.getEntry().getKeyHash(), user, FilteredThreadIndex.GROUP_FAVORITE));
-                out.write("\" title=\"Add as a friend\"><img src=\"images/addToFavorites.png\" alt=\"friend\" border=\"0\" /></a>\n");
-                out.write("/<a href=\"");
-                out.write(getAddToGroupLink(req, node.getEntry().getKeyHash(), user, FilteredThreadIndex.GROUP_IGNORE));
-                out.write("\" title=\"Add to killfile\"><img src=\"images/addToIgnored.png\" alt=\"ignore\" border=\"0\" /></a>)\n");
-            }
-        }
-
-        out.write(": ");
-        out.write("<a href=\"");
-        if (false) {
-            out.write(getViewPostLink(req, node, user, false));
-        } else {
-            out.write(getViewThreadLink(req, node, user));
-        }
-        out.write("\" title=\"View post\">");
-        EntryContainer entry = archive.getEntry(node.getEntry());
-        if (entry == null) throw new RuntimeException("Unable to fetch the entry " + node.getEntry());
-
-        HeaderReceiver rec = new HeaderReceiver();
-        parser.parse(entry.getEntry().getText(), rec);
-        String subject = rec.getHeader(HTMLRenderer.HEADER_SUBJECT);
-        if ( (subject == null) || (subject.trim().length() <= 0) )
-            subject = "(no subject)";
-        if (displayed) {
-            // currently being rendered
-            out.write("<b>");
-            out.write(trim(subject, 40));
-            out.write("</b>");
-        } else {
-            out.write(trim(subject, 40));
-        }
-        //out.write("</a>\n</td><td class=\"threadRight\">\n");
-        out.write("</a>");
-        if (false) {
-            out.write(" (<a href=\"");
-            out.write(getViewThreadLink(req, node, user));
-            out.write("\" title=\"View all posts in the thread\">full thread</a>)\n");
-        }
-        
-        out.write("</span><span class=\"threadInfoRight\">");
-        
-        out.write(" <a href=\"");
-        BlogURI newestURI = new BlogURI(node.getMostRecentPostAuthor(), node.getMostRecentPostDate());
-        if (false) {
-            out.write(getViewPostLink(req, newestURI, user));
-        } else {
-            List paths = new ArrayList();
-            paths.add(node);
-            ThreadNode cur = null;
-            while (paths.size() > 0) {
-                cur = (ThreadNode)paths.remove(0);
-                if (cur.getEntry().equals(newestURI))
-                    break;
-                for (int i = cur.getChildCount() - 1; i >= 0; i--)
-                    paths.add(cur.getChild(i));
-                if (paths.size() <= 0)
-                    cur = null;
-            }
-            if (cur != null)
-                out.write(getViewThreadLink(req, cur, user));
-        }
-        out.write("\" title=\"View the most recent post\">latest - ");
-
-        long dayBegin = BlogManager.instance().getDayBegin();
-        long postId = node.getMostRecentPostDate();
-        if (postId >= dayBegin) {
-            out.write("<b>today</b>");
-        } else if (postId >= dayBegin - 24*60*60*1000) {
-            out.write("<b>yesterday</b>");
-        } else {
-            int daysAgo = (int)((dayBegin - postId + 24*60*60*1000-1)/(24*60*60*1000));
-            out.write(daysAgo + " days ago");
-        }
-
-        out.write("</a>\n");
-        /*
-        out.write(" <a href=\"");
-        out.write(getViewThreadLink(req, node, user));
-        out.write("\" title=\"View all posts in the thread\">full thread</a>\n");
-         */
-        out.write("</span>");
-        out.write("</td></tr>\n");
-        
-        boolean rendered = true;
-        
-        if (showChildren) {
-            for (int i = 0; i < node.getChildCount(); i++) {
-                ThreadNode child = node.getChild(i);
-                boolean childRendered = renderThread(user, out, index, archive, req, child, depth+1, visibleEntry, state, visibleURIs);
-                rendered = rendered || childRendered;
-            }
-        }
-        
-        return rendered;
-    }
-    
-    private String getFlagHTML(User user, ThreadNode node) {
-        if ( (user.getBlog() != null) && (node.containsAuthor(user.getBlog())) )
-            return "<img src=\"images/self.png\" border=\"0\" alt=\"You have posted in the thread\" />";
-        
-        // grab all of the peers in the user's favorites group and check to see if 
-        // they posted something in the given thread, flagging it if they have
-        boolean favoriteFound = false;
-        for (Iterator iter = user.getPetNameDB().getNames().iterator(); iter.hasNext(); ) {
-            PetName pn = user.getPetNameDB().getByName((String)iter.next());
-            if (pn.isMember(FilteredThreadIndex.GROUP_FAVORITE)) {
-                Hash cur = new Hash();
-                try {
-                    cur.fromBase64(pn.getLocation());
-                    if (node.containsAuthor(cur)) {
-                        favoriteFound = true;
-                        break;
-                    }
-                } catch (Exception e) {}
-            }
-        }
-        if (favoriteFound) 
-            return "<img src=\"images/favorites.png\" border=\"0\" alt=\"flagged author posted in the thread\" />";
-        else
-            return "&nbsp;"; 
-    }
-    
-    protected String getTitle() { return "Syndie :: View threads"; }
-}
diff --git a/apps/syndie/jetty-syndie.xml b/apps/syndie/jetty-syndie.xml
deleted file mode 100644
index 4cf61e494ef7198c34dba1d7ea75ba14a27ea5d1..0000000000000000000000000000000000000000
--- a/apps/syndie/jetty-syndie.xml
+++ /dev/null
@@ -1,114 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1" ?> 
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure 1.2//EN" "http://jetty.mortbay.org/configure_1_2.dtd">
-
-
-<!-- =============================================================== -->
-<!-- Configure the Jetty Server                                      -->
-<!-- =============================================================== -->
-<Configure class="org.mortbay.jetty.Server">
-
-  <!-- =============================================================== -->
-  <!-- Configure the Request Listeners                                 -->
-  <!-- =============================================================== -->
-
-  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
-  <!-- Add and configure a HTTP listener to port 8080                  -->
-  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
-  <Call name="addListener">
-    <Arg>
-      <New class="org.mortbay.http.SocketListener">
-        <Arg>
-          <New class="org.mortbay.util.InetAddrPort">
-            <Set name="host">0.0.0.0</Set>
-            <Set name="port">8001</Set>
-          </New>
-        </Arg>
-        <Set name="MinThreads">3</Set>
-        <Set name="MaxThreads">10</Set>
-        <Set name="MaxIdleTimeMs">30000</Set>
-        <Set name="LowResourcePersistTimeMs">1000</Set>
-        <Set name="ConfidentialPort">8443</Set>
-        <Set name="IntegralPort">8443</Set>
-        <Set name="PoolName">main</Set>
-      </New>
-    </Arg>
-  </Call>
-
-
-  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
-  <!-- Add a HTTPS SSL listener on port 8443                           -->
-  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
-  <!-- UNCOMMENT TO ACTIVATE
-  <Call name="addListener">
-    <Arg>
-      <New class="org.mortbay.http.SunJsseListener">
-        <Set name="Port">8443</Set>
-        <Set name="PoolName">main</Set>
-        <Set name="Keystore"><SystemProperty name="jetty.home" default="."/>/etc/demokeystore</Set>
-	<Set name="Password">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
-	<Set name="KeyPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set>
-        <Set name="NonPersistentUserAgent">MSIE 5</Set>
-      </New>
-    </Arg>
-  </Call>
-  -->
-
-  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
-  <!-- Add a AJP13 listener on port 8009                               -->
-  <!-- This protocol can be used with mod_jk in apache, IIS etc.       -->
-  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
-  <!--
-  <Call name="addListener">
-    <Arg>
-      <New class="org.mortbay.http.ajp.AJP13Listener">
-        <Set name="PoolName">ajp</Set>
-        <Set name="Port">8009</Set>
-        <Set name="MinThreads">3</Set>
-        <Set name="MaxThreads">20</Set>
-        <Set name="MaxIdleTimeMs">0</Set>
-        <Set name="confidentialPort">443</Set>
-      </New>
-    </Arg>
-  </Call>
-  -->
-
-
-  <!-- =============================================================== -->
-  <!-- Configure the Contexts                                          -->
-  <!-- =============================================================== -->
-
-
-  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
-  <!-- Add a all web application within the webapps directory.         -->
-  <!-- + No virtual host specified                                     -->
-  <!-- + Look in the webapps directory relative to jetty.home or .     -->
-  <!-- + Use the default webdefault.xml in jetty's install             -->
-  <!-- + Upack the war file                                            -->
-  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
-  <Set name="rootWebApp">syndie</Set>
-  <Call name="addWebApplication">
-    <Arg>/</Arg>
-    <Arg>syndie.war</Arg>
-  </Call>
-
-  <!-- =============================================================== -->
-  <!-- Configure the Request Log                                       -->
-  <!-- =============================================================== -->
-  <Set name="RequestLog">
-    <New class="org.mortbay.http.NCSARequestLog">
-      <Arg>./logs/yyyy_mm_dd.syndie-request.log</Arg>
-      <Set name="retainDays">90</Set>
-      <Set name="append">true</Set>
-      <Set name="extended">false</Set>
-      <Set name="buffered">false</Set>
-      <Set name="LogTimeZone">GMT</Set>
-    </New>
-  </Set>
-
-  <!-- =============================================================== -->
-  <!-- Configure the Other Server Options                              -->
-  <!-- =============================================================== -->
-  <Set name="requestsPerGC">2000</Set>
-  <Set name="statsOn">false</Set>
-
-</Configure>
diff --git a/apps/syndie/jsp/_bodyindex.jsp b/apps/syndie/jsp/_bodyindex.jsp
deleted file mode 100644
index d997bbd2c05ab3d74627e48f990c20a8db24aa7b..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/_bodyindex.jsp
+++ /dev/null
@@ -1,40 +0,0 @@
-<%@page contentType="text/html; charset=UTF-8" import="net.i2p.syndie.web.ArchiveViewerBean, net.i2p.syndie.*, net.i2p.client.naming.PetName" %>
-<% request.setCharacterEncoding("UTF-8"); %>
-<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" /><%
-if (user.getAuthenticated() && (null != request.getParameter("action")) ) {
-  %><!-- <%=request.getParameterMap()%> --><%
-  String blog = request.getParameter("blog");
-  String group = null;
-  if (request.getParameter("action").equals("Bookmark blog"))
-    group = "Favorites";
-  else if (request.getParameter("action").equals("Ignore blog"))
-    group = "Ignore";
-  boolean unignore = ("Unignore blog".equals(request.getParameter("action")));
-
-  PetName pn = user.getPetNameDB().getByLocation(blog);
-  String name = null;
-  if (pn != null) name = pn.getName();
-  if (name == null)
-    name = request.getParameter("name");
-  if (name == null)
-    name = blog;
-  if ( (name != null) && (blog != null) && ( (group != null) || (unignore) ) ) {
-    if (pn != null) {
-      if (unignore)
-        pn.removeGroup("Ignore");
-      else
-        pn.addGroup(group);
-    } else {
-      pn = new PetName(name, "syndie", "syndieblog", blog);
-      pn.addGroup(group);
-      user.getPetNameDB().add(pn);
-    }
-    BlogManager.instance().saveUser(user);
-  }
-}
-%><table border="0" width="100%" class="b_content">
-<tr class="b_content"><form action="index.jsp"><td nowrap="nowrap">
-<em class="b_selectorTitle">Blogs:</em> <span class="b_selector"><%ArchiveViewerBean.renderBlogSelector(user, request.getParameterMap(), out);%></span>
-<input type="submit" value="Refresh" class="b_selectorRefresh" />
-<input type="submit" name="action" value="<%=ArchiveViewerBean.SEL_ACTION_SET_AS_DEFAULT%>" class="b_selectorDefault" />
-<%ArchiveViewerBean.renderBlogs(user, request.getParameterMap(), out, "</td></form></tr><tr><td align=\"left\" valign=\"top\">");%></td></tr></table>
\ No newline at end of file
diff --git a/apps/syndie/jsp/_leftnav.jsp b/apps/syndie/jsp/_leftnav.jsp
deleted file mode 100644
index 2b342a5f539785917ac1a7524d0333600d18c8a4..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/_leftnav.jsp
+++ /dev/null
@@ -1,3 +0,0 @@
-<%@page import="net.i2p.syndie.web.ArchiveViewerBean, net.i2p.syndie.*, net.i2p.data.Base64" %>
-<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
-<jsp:useBean scope="session" class="net.i2p.syndie.data.TransparentArchiveIndex" id="archive" />
\ No newline at end of file
diff --git a/apps/syndie/jsp/_rightnav.jsp b/apps/syndie/jsp/_rightnav.jsp
deleted file mode 100644
index 70f597e4ffa31fba908c72634f3b80740a5c69e2..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/_rightnav.jsp
+++ /dev/null
@@ -1 +0,0 @@
-<!-- nada -->
diff --git a/apps/syndie/jsp/_toplogo.jsp b/apps/syndie/jsp/_toplogo.jsp
deleted file mode 100644
index 07d6f60abce908cafd0d16a8c7881c1cb6e4e307..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/_toplogo.jsp
+++ /dev/null
@@ -1,5 +0,0 @@
-<%@page import="net.i2p.syndie.BlogManager" %>
-<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
-<!--
-<center>[syndiemedia]</center>
--->
\ No newline at end of file
diff --git a/apps/syndie/jsp/_topnav.jsp b/apps/syndie/jsp/_topnav.jsp
deleted file mode 100644
index 2f3536513b8de48e3d6f2780133fabfac29c2d51..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/_topnav.jsp
+++ /dev/null
@@ -1,47 +0,0 @@
-<%@page import="net.i2p.syndie.*, net.i2p.syndie.sml.*, net.i2p.syndie.web.*" %>
-<jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" />
-<form action="<%=request.getRequestURI() + "?" + (request.getQueryString() != null ? request.getQueryString() : "")%>">
-<td nowrap="nowrap" colspan="2" height="10" class="b_topnav">
-<span class="b_topnavHome"><a href="index.jsp" class="b_topnavHome">Home</a></span>
-<a href="admin.jsp" class="b_topnavAdmin">Syndie admin</a>
-<a href="remote.jsp" class="b_topnavRemote">Remote archives</a>
-<a href="rssimport.jsp" class="b_topnavRSSImport">RSS imports</a>
-<a href="import.jsp" class="b_topnavImport">Import</a>
-</td><td nowrap="nowrap" height="10" class="b_topnavUser"><%
-if ("true".equals(request.getParameter("logout"))) {
-  user.invalidate();
-  RemoteArchiveBean rem = (RemoteArchiveBean)session.getAttribute("remote");
-  if (rem != null) rem.reinitialize();
-  PostBean post = (PostBean)session.getAttribute("post");
-  if (post != null) post.reinitialize();
-}
-String login = request.getParameter("login");
-String pass = request.getParameter("password");
-String loginSubmit = request.getParameter("Login");
-if ( (login != null) && (pass != null) && (loginSubmit != null) && (loginSubmit.equals("Login")) ) {
-  String loginResult = BlogManager.instance().login(user, login, pass);
-  if (!user.getAuthenticated())
-    out.write("<b class=\"b_topnavLoginResult\">" + loginResult + "</b>");
-}
-%>
-<% if (user.getAuthenticated()) { %>
-<span class="b_topnavUsername">Logged in as:</span> <em class="b_topnavUsername"><jsp:getProperty property="username" name="user" />:</em>
-<a class="b_topnavBlog" href="<%=""/*HTMLRenderer.getPageURL(user.getBlog(), null, -1, -1, -1, user.getShowExpanded(), user.getShowImages())*/%>"><%=HTMLRenderer.sanitizeString(ArchiveViewerBean.getBlogName(user.getBlogStr()))%></a>
-<a class="b_topnavPost" href="<%=""/*HTMLRenderer.getPostURL(user.getBlog())*/%>">Post</a>
-<a class="b_topnavMeta" href="<%=""/*HTMLRenderer.getMetadataURL(user.getBlog())*/%>">Metadata</a>
-<a class="b_topnavAddr" href="addresses.jsp">Addressbook</a>
-<a class="b_topnavLogout" href="index.jsp?logout=true">Logout</a>
-<%} else {%>
-<span class="b_topnavLogin">Login:</span> <input class="b_topnavLogin" type="text" name="login" size="8" />
-<span class="b_topnavPass">Pass:</span> <input class="b_topnavPass" type="password" name="password" size="8" /><%
-java.util.Enumeration params = request.getParameterNames();
-while (params.hasMoreElements()) {
- String p = (String)params.nextElement();
- String val = request.getParameter(p); 
- %><input type="hidden" name="<%=p%>" value="<%=val%>" /><%
-}%>
-<input class="b_topnavLoginSubmit" type="submit" name="Login" value="Login" />
-<a class="b_topnavRegister" href="register.jsp">Register</a>
-<% } %>
-</td>
-</form> 
\ No newline at end of file
diff --git a/apps/syndie/jsp/about.html b/apps/syndie/jsp/about.html
deleted file mode 100644
index 329b5a66fb04372b95b52a63c36b9d390244da26..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/about.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html
-><head><title>What is Syndie?</title></head
-><body bgcolor="#BBBBFF">
-
-<p>Perhaps the best introduction to Syndie can be found <a
-href="blog.jsp?blog=ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=&entry=ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=/1132012800001">in Syndie itself</a>.</p>
-
-<p>Updates can be found by filtering for the <a
-href="threads.jsp?tags=syndie.intro">syndie.intro</a> tag (if you only want to
-receive posts that <a
-href="threads.jsp?author=ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=">jrandom</a>
- made with that tag, that can be <a
-href="threads.jsp?tags=syndie.intro&author=ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=">achieved</a>
-as well).</p>
-
-<p>If you have any questions or problems with Syndie, just post them and
-syndicate it up to <a href="http://syndiemedia.i2p/">syndiemedia.i2p</a> (which should show up as the default archive
-on new installs).  You can also use the <a href="http://forum.i2p.net/">I2P
-forums</a> if you're having trouble getting Syndie to work, and people are
-almost always around on the <a href="http://forum.i2p.net/viewtopic.php?t=952">#i2p irc
-channel</a>.</p>
-
-<p>One FAQ which might keep people from getting more posts into their Syndie
-node regards cookies.  If you get "internal errors" when using the <a
-href="syndicate.jsp">syndicate form</a>, you probably have cookies disabled. 
-Syndie needs cookies to help maintain state, and while its good practice to
-disable cookies in general, you should be able to tell your web browser to make
-an exception and allow cookies to "localhost" (or wherever your Syndie instance
-is).  Further FAQs should be found
-<a href="threads.jsp?tags=syndie.faq">within syndie</a></p>
-</body></html>
diff --git a/apps/syndie/jsp/images/addToFavorites.png b/apps/syndie/jsp/images/addToFavorites.png
deleted file mode 100644
index 95ded8d979a2c86a3a261636862795ab52fc2f3c..0000000000000000000000000000000000000000
Binary files a/apps/syndie/jsp/images/addToFavorites.png and /dev/null differ
diff --git a/apps/syndie/jsp/images/addToIgnored.png b/apps/syndie/jsp/images/addToIgnored.png
deleted file mode 100644
index 5b87bf45bc46333e5fffc775cf475e17620a9834..0000000000000000000000000000000000000000
Binary files a/apps/syndie/jsp/images/addToIgnored.png and /dev/null differ
diff --git a/apps/syndie/jsp/images/collapse.png b/apps/syndie/jsp/images/collapse.png
deleted file mode 100644
index 2ce31b36d88ff5b3a23caaeb92cd9b3a5557fcce..0000000000000000000000000000000000000000
Binary files a/apps/syndie/jsp/images/collapse.png and /dev/null differ
diff --git a/apps/syndie/jsp/images/default_blog_logo.png b/apps/syndie/jsp/images/default_blog_logo.png
deleted file mode 100644
index 54ea2a91766e997c872302000f29c06e32bf64d5..0000000000000000000000000000000000000000
Binary files a/apps/syndie/jsp/images/default_blog_logo.png and /dev/null differ
diff --git a/apps/syndie/jsp/images/expand.png b/apps/syndie/jsp/images/expand.png
deleted file mode 100644
index 95c0c4c81a2db8c4d58842c15e1a0d08148fab9b..0000000000000000000000000000000000000000
Binary files a/apps/syndie/jsp/images/expand.png and /dev/null differ
diff --git a/apps/syndie/jsp/images/favorites.png b/apps/syndie/jsp/images/favorites.png
deleted file mode 100644
index 5fa5a83bb3356281a22a77ba14d2eb1f3fc5fe66..0000000000000000000000000000000000000000
Binary files a/apps/syndie/jsp/images/favorites.png and /dev/null differ
diff --git a/apps/syndie/jsp/images/noSubthread.png b/apps/syndie/jsp/images/noSubthread.png
deleted file mode 100644
index 3ea13d3a131c793fc3cabe91a9861d69767a2164..0000000000000000000000000000000000000000
Binary files a/apps/syndie/jsp/images/noSubthread.png and /dev/null differ
diff --git a/apps/syndie/jsp/images/self.png b/apps/syndie/jsp/images/self.png
deleted file mode 100644
index f56c8a0df06c47f73c629521d8028be6ddac8970..0000000000000000000000000000000000000000
Binary files a/apps/syndie/jsp/images/self.png and /dev/null differ
diff --git a/apps/syndie/jsp/images/syndielogo.png b/apps/syndie/jsp/images/syndielogo.png
deleted file mode 100644
index 9ed9b852add66f838d4d35a138b265922ddfd8c2..0000000000000000000000000000000000000000
Binary files a/apps/syndie/jsp/images/syndielogo.png and /dev/null differ
diff --git a/apps/syndie/jsp/images/threadIndent.png b/apps/syndie/jsp/images/threadIndent.png
deleted file mode 100644
index 3ea13d3a131c793fc3cabe91a9861d69767a2164..0000000000000000000000000000000000000000
Binary files a/apps/syndie/jsp/images/threadIndent.png and /dev/null differ
diff --git a/apps/syndie/jsp/import.jsp b/apps/syndie/jsp/import.jsp
deleted file mode 100644
index ba482bd80fde2615cff6454e59d677fd1ae3e590..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/import.jsp
+++ /dev/null
@@ -1,68 +0,0 @@
-<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.data.Base64, net.i2p.syndie.web.*, net.i2p.syndie.sml.*, net.i2p.syndie.data.*, net.i2p.syndie.*, org.mortbay.servlet.MultiPartRequest, java.util.*, java.io.*" %><%
-request.setCharacterEncoding("UTF-8"); 
-%><jsp:useBean scope="session" class="net.i2p.syndie.data.TransparentArchiveIndex" id="archive" 
-/><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
-<html>
-<head>
-<title>SyndieMedia import</title>
-<link href="style.jsp" rel="stylesheet" type="text/css" >
-</head>
-<body>
-<table border="1" cellpadding="0" cellspacing="0" width="100%">
-<tr class="b_toplogo"><td colspan="5" valign="top" align="left" class="b_toplogo"><jsp:include page="_toplogo.jsp" /></td></tr>
-<tr><td valign="top" align="left" rowspan="2" class="b_leftnav"><jsp:include page="_leftnav.jsp" /></td>
-    <jsp:include page="_topnav.jsp" />
-    <td valign="top" align="left" rowspan="2" class="b_rightnav"><jsp:include page="_rightnav.jsp" /></td></tr>
-<tr class="b_content"><td valign="top" align="left" colspan="3" class="b_content"><%
-
-String contentType = request.getContentType();
-if ((contentType != null) && (contentType.indexOf("boundary=") != -1) ) {
-    MultiPartRequest req = new MultiPartRequest(request);
-    int metaId = 0;
-    while (true) {
-      InputStream meta = req.getInputStream("blogmeta" + metaId);
-      if ( (meta == null) || (meta.available() <= 0) )
-        break;
-      if (!BlogManager.instance().importBlogMetadata(meta)) {
-        %><span class="b_importMsgErr">Metadata <%=metaId%> failed to be imported</span><br /><%
-        break;
-       }
-      metaId++;
-    }
-    int entryId = 0;
-    while (true) {
-      InputStream entry = req.getInputStream("blogpost" + entryId);
-      if ( (entry == null) || (entry.available() <= 0) )
-        break;
-      if (!BlogManager.instance().importBlogEntry(entry)) {
-        %><span class="b_importMsgErr">Entry <%=entryId%> failed to be imported</span><br /><%
-        break;
-      }
-      entryId++;
-    }
-
-    if ( (entryId > 0) || (metaId > 0) ) {
-      BlogManager.instance().getArchive().regenerateIndex();
-      session.setAttribute("index", BlogManager.instance().getArchive().getIndex());
-    }
-%><span class="b_importMsgOk">Imported <%=entryId%> posts and <%=metaId%> blog metadata files.</span>
-<% 
-} else { %><form action="import.jsp" method="POST" enctype="multipart/form-data"> 
-<span class="b_importField">Blog metadata 0:</span> <input class="b_importField" type="file" name="blogmeta0" /><br />
-<span class="b_importField">Blog metadata 1:</span> <input class="b_importField" type="file" name="blogmeta1" /><br />
-<span class="b_importField">Post 0:</span> <input class="b_importField" type="file" name="blogpost0" /><br />
-<span class="b_importField">Post 1:</span> <input class="b_importField" type="file" name="blogpost1" /><br />
-<span class="b_importField">Post 2:</span> <input class="b_importField" type="file" name="blogpost2" /><br />
-<span class="b_importField">Post 3:</span> <input class="b_importField" type="file" name="blogpost3" /><br />
-<span class="b_importField">Post 4:</span> <input class="b_importField" type="file" name="blogpost4" /><br />
-<span class="b_importField">Post 5:</span> <input class="b_importField" type="file" name="blogpost5" /><br />
-<span class="b_importField">Post 6:</span> <input class="b_importField" type="file" name="blogpost6" /><br />
-<span class="b_importField">Post 7:</span> <input class="b_importField" type="file" name="blogpost7" /><br />
-<span class="b_importField">Post 8:</span> <input class="b_importField" type="file" name="blogpost8" /><br />
-<span class="b_importField">Post 9:</span> <input class="b_importField" type="file" name="blogpost9" /><br />
-<hr />
-<input class="b_importSubmit" type="submit" name="Post" value="Post entry" /> <input class="b_importCancel" type="reset" value="Cancel" />
-<% } %>
-</td></tr>
-</table>
-</body>
diff --git a/apps/syndie/jsp/index.html b/apps/syndie/jsp/index.html
deleted file mode 100644
index 7244e85bf61f171c2773019970a3eb31b10d3598..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/index.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html
-><head><title>Welcome to Syndie</title></head
-><body bgcolor="#BBBBFF"><div align="center" valign="center">
-<table border="0"><tr><td valign="top">
-<img src="images/syndielogo.png" alt="" /></td>
-<td valign="top" align="left"><b>Welcome to Syndie!</b><br />
-
-<p>Jump right in and read <a href="threads.jsp">discussion threads</a> or
-<a href="blogs.jsp">blogs</a></p>
-<p><a href="post.jsp">Create</a> a new post of your own</p>
-<p><a href="about.html">Learn more</a> about Syndie</p>
-<p><b>NOTE:</b> This version of Syndie is being replaced by
-<a href="http://syndie.i2p.net">the new Syndie</a>!
-The new Syndie is a standalone application under active development.
-Please give the new Syndie a try, as it has lots more traffic
-than this version. Don't expect anybody to see your posts here.</p>
-</td></tr></table>
-</div>
-</body></html>
diff --git a/apps/syndie/jsp/register.jsp b/apps/syndie/jsp/register.jsp
deleted file mode 100644
index 660735ed430374d7f212c44c3752519f61bad869..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/register.jsp
+++ /dev/null
@@ -1,50 +0,0 @@
-<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.data.Base64, net.i2p.syndie.web.*, net.i2p.syndie.sml.*, net.i2p.syndie.*" %><% 
-request.setCharacterEncoding("UTF-8"); 
-%><jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" 
-/><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
-<html>
-<head>
-<title>SyndieMedia</title>
-<link href="style.jsp" rel="stylesheet" type="text/css" >
-</head>
-<body>
-<table border="1" cellpadding="0" cellspacing="0" width="100%">
-<tr class="b_toplogo"><td colspan="5" valign="top" align="left" class="b_toplogo"><jsp:include page="_toplogo.jsp" /></td></tr>
-<tr><td valign="top" align="left" rowspan="2" class="b_leftnav"><jsp:include page="_leftnav.jsp" /></td>
-    <jsp:include page="_topnav.jsp" />
-    <td valign="top" align="left" rowspan="2" class="b_rightnav"><jsp:include page="_rightnav.jsp" /></td></tr>
-<tr class="b_content"><td valign="top" align="left" colspan="3" class="b_content"><%
-String regLogin = request.getParameter("login");
-boolean showForm = true;
-if ( (regLogin != null) && ("Register".equals(request.getParameter("Register"))) ) {
-  String regUserPass = request.getParameter("password");
-  String regPass = request.getParameter("registrationpassword");
-  String blogName = request.getParameter("blogname");
-  String desc = request.getParameter("description");
-  String url = request.getParameter("contacturl");
-  String regResult = BlogManager.instance().register(user, regLogin, regUserPass, regPass, blogName, desc, url);
-  if (User.LOGIN_OK.equals(regResult)) {
-    %><span class="b_regMsgOk">Registration successful.</span> <a class="b_reg" href="index.jsp">Continue...</a>
-<%  showForm = false;
-  } else {
-    %><span class="b_regMsgErr"><%=regResult%></span><%
-  }
-} 
-if (showForm) {%><form action="register.jsp" method="POST">
-<p class="b_reg">To create a new blog (and Syndie user account), please fill out the following form.  
-You may need to enter a registration password given to you by this Syndie instance's
-operator, or there may be no registration password in place (in which case you can
-leave that field blank).</p>
-<p class="b_reg">
-<em class="b_regField">Syndie login:</em> <input class="b_regField" type="text" size="8" name="login" /><br />
-<em class="b_regField">New password:</em> <input class="b_regField" type="password" size="8" name="password" /><br />
-<em class="b_regField">Registration password:</em> <input class="b_regField" type="password" size="8" name="registrationpassword" /><br />
-<em class="b_regField">Blog name:</em> <input class="b_regField" type="text" size="32" name="blogname" /><br />
-<em class="b_regField">Brief description:</em> <input class="b_regField" type="text" size="60" name="description" /><br />
-<em class="b_regField">Contact URL:</em> <input class="b_regField" type="text" size="20" name="contacturl" /> <span class="b_reg">(e.g. mailto://user@mail.i2p, http://foo.i2p/, etc)</span><br />
-<input class="b_regSubmit" type="submit" name="Register" value="Register" />
-</p>
-</form><% } %>
-</td></tr>
-</table>
-</body>
\ No newline at end of file
diff --git a/apps/syndie/jsp/smlref.jsp b/apps/syndie/jsp/smlref.jsp
deleted file mode 100644
index ed2bd52ee5b1059ed2d8805eb95d7391ef5b1b20..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/smlref.jsp
+++ /dev/null
@@ -1,38 +0,0 @@
-<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.util.*" %><% 
-request.setCharacterEncoding("UTF-8"); 
-%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
-<html>
-<head>
-<title>SML Quick Reference</title>
-<link href="style.jsp" rel="stylesheet" type="text/css" >
-</head>
-<body>
-<h3>SML Quick Reference:</h3>
-<ul>
-  <li>newlines are newlines are newlines.</li>
-  <li>all &lt; and &gt; are replaced with their &amp;symbol;</li>
-  <li>the [ and ] characters delimit tags, or must be quoted by doubling them up ([[ displays as [, ]] displays as ])</li>
-  <li>[b][/b] = &lt;b&gt;bold&lt;/b&gt;</li>
-  <li>[i][/i] = &lt;i&gt;italics&lt;/i&gt;</li>
-  <li>[u][/u] = &lt;i&gt;underline&lt;/i&gt;</li>
-  <li>[pre]foo[/pre] = &lt;pre&gt;preformatted section&lt;/pre&gt;</li>
-  <li>[cut]more inside[/cut] = &lt;a href="#"&gt;more inside...&lt;/a&gt;</li>
-  <li>[quote][/quote] = Quoted text</li>
-  <li>[img attachment="1"]alt[/img] = use attachment 1 as an image with 'alt' as the alt text.</li>
-  <li>[attachment id="0"]text[/attachment] = offer attachment 0 as a link in your post</li>
-  <li>[attachment thumbnail="0" id="1"]text[/attachment] = offer attachment 1 as a link around a thumbnail image using attachment 0</li>
-  <li>[link schema="eep" location="http://forum.i2p"]text[/link] = offer a link to an external resource (accessible with the given schema)</li>
-  <li>[blog name="name" bloghash="base64hash"]description[/blog] = link to all posts in the blog</li>
-  <li>[blog name="name" bloghash="base64hash" blogentry="1234"]description[/blog] = link to the specified post in the blog</li>
-  <li>[blog name="name" bloghash="base64hash" blogtag="tag"]description[/blog] = link to all posts in the blog with the specified tag</li>
-  <li>[blog name="name" blogtag="tag"]description[/blog] = link to all posts in all blogs with the specified tag</li>
-  <li>[archive name="name" description="they have good stuff" schema="eep" location="http://syndiemedia.i2p/archive/archive.txt"]foo![/archive] = offer an easy way to sync up with a new Syndie archive</li>
-  <li>[address name="www.i2p" location="Nf3ab-ZFkmI-LyMt7Gjg...vobM57UpqSAAAA" schema="i2p" proto="eep"]official website[/address] = share a pet name reference to the given eepsite (using fields from the addresses page)</li>
-</ul>
-SML headers are newline delimited key:value pairs.  Example keys are:
-<ul>
-  <li>bgcolor = background color of the post (e.g. bgcolor:#ffccaa or bgcolor=red)</li>
-  <li>bgimage = attachment number to place as the background image for the post (only shown if images are enabled) (e.g. bgimage=1)</li>
-  <li>textfont = font to put most text into</li>
-</ul>
-</body>
diff --git a/apps/syndie/jsp/style.jsp b/apps/syndie/jsp/style.jsp
deleted file mode 100644
index c1b37f3ee4cdf466b7a21c8ecf0b84700a0376ab..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/style.jsp
+++ /dev/null
@@ -1,7 +0,0 @@
-<%@page contentType="text/css; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.util.FileUtil" %>
-<% request.setCharacterEncoding("UTF-8"); %>
-<%@include file="syndie.css" %>
-<% 
-String content = FileUtil.readTextFile("./docs/syndie_standard.css", -1, true); 
-if (content != null) out.write(content);
-%>
\ No newline at end of file
diff --git a/apps/syndie/jsp/syndie.css b/apps/syndie/jsp/syndie.css
deleted file mode 100644
index 3d7ccef2473c70117c39270b791c342862582e93..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/syndie.css
+++ /dev/null
@@ -1,444 +0,0 @@
-.b_topnavUser {
-   text-align: right;
-   background-color: #CCCCDD;
-}
-.b_topnavHome {
-   background-color: #CCCCDD;
-   color: #000000;
-   width: 50px;
-   text-align: left;
-}
-.b_topnav {
-   background-color: #CCCCDD;
-}
-.b_content {
-}
-.s_summary_overall {
-}
-.s_detail_overall {
-}
-.s_detail_subject {
-    font-size: 0.8em;
-    text-align: left;
-    background-color: #BBBBFF;
-}
-.s_detail_quote {
-    margin-left: 1em;
-    border: 1px solid #DBDBDB;
-    background-color: #E0E0E0;
-}
-.s_detail_italic {
-    font-style: italic;
-}
-.s_detail_bold {
-    font-style: normal;
-    font-weight: bold;
-}
-.s_detail_underline {
-    font-style: normal;
-    text-decoration: underline;
-}
-.s_detail_meta {
-   font-size: 0.8em;
-   text-align: right;
-   background-color: #BBBBFF;
-}
-
-.s_summary_subject {
-   font-size: 0.8em;
-   text-align: left;
-   background-color: #BBBBFF;
-}
-.s_summary_meta {
-   font-size: 0.8em;
-   text-align: right;
-   background-color: #BBBBFF;
-}
-.s_summary_quote {
-    margin-left: 1em;
-    border-width: 1px solid #DBDBDB;
-    background-color: #E0E0E0;
-}
-.s_summary_italic {
-    font-style: italic;
-}
-.s_summary_bold {
-    font-style: normal;
-    font-weight: bold;
-}
-.s_summary_underline {
-    font-style: normal;
-    text-decoration: underline;
-}
-.s_summary_summDetail {
-   font-size: 0.8em;
-}
-.s_detail_summDetail {
-}
-.s_detail_summDetailBlog {
-}
-.s_detail_summDetailBlogLink {
-}
-td.s_detail_summDetail {
-   background-color: #DDDDFF;
-}
-td.s_summary_summ {
-   font-size: 0.8em;
-   background-color: #DDDDFF;
-}
-
-<!-- following are doubtful salmon's contributions -->
-body {
-	margin :  0px;
-	padding : 0px;
-	width: 99%;
-	font-family : Arial, sans-serif, Helvetica;
-	background-color : #FFF;
-	color :  black;
-   font-size : 100%;
-    
-    /* we've avoided Tantek Hacks so far,
-    ** but we can't avoid using the non-w3c method of 
-    ** box rendering. (and therefore one of mozilla's
-    ** proprietry -moz properties (which hopefully they'll
-    ** drop soon).
-    */
-    -moz-box-sizing : border-box;
-    box-sizing :      border-box;
-}
-a:link{color:#007}
-a:visited{color:#606}
-a:hover{color:#720}
-a:active{color:#900}
-
-select {
-    min-width: 1.5em;
-}
-.overallTable {
-    border-spacing: 0px;
-    border-collapse: collapse;
-    float: left;
-}
-.topNav {
-   background-color: #BBB;
-}
-.topNav_user {
-   text-align: left;
-   float: left;
-   display: inline;
-}
-.topNav_admin {
-   text-align: right;
-   float: right;
-   margin: 0 5px 0 0;
-   display: inline;
-}
-.controlBar {
-        border-bottom: thick double #CCF;
-        border-left: medium solid #CCF;
-        border-right: medium solid #CCF;
-	background-color: #EEF;
-	color: inherit;
-	font-size: small;
-	clear: left; /* fixes a bug in Opera */
-}
-.controlBarRight {
-   text-align: right;
-}
-.threadEven {
-   background-color: #FFF;
-   white-space: nowrap;
-}
-.threadOdd {
-   background-color: #FFC;
-   white-space: nowrap;
-}
-.threadLeft {
-   text-align: left;
-   align: left;
-}
-.threadNav {
-   background-color: #EEF;
-   border: medium solid #CCF;
-}
-.threadNavRight {
-   text-align: right;
-   float: right;
-   background-color: #EEF;
-}
-.rightOffset {
-    float: right;
-    margin: 0 5px 0 0;
-    display: inline;
-}
-.threadInfoLeft {
-    float: left;
-    margin: 5px 0px 0 0;
-    display: inline;
-}
-.threadInfoRight {
-    float: right;
-    margin: 0 5px 0 0;
-    display: inline;
-}
-.postMeta {
-    border-top: 1px solid black;
-    background-color: #FFB;
-}
-.postMetaSubject {
-   text-align: left;
-   font-size: large;
-}
-.postMetaLink {
-   text-align: right;
-}
-.postDetails {
-   background-color: #FFC;
-}
-.postReply {
-   background-color: #CCF;
-}
-.postReplyText {
-   background-color: #CCF;
-}
-.postReplyOptions {
-   background-color: #CCF;
-}
-.syndieBlogTopNav {
-	padding: 0.5em;
-	width: 98%;
-	border: medium solid #CCF;
-	background-color: #EEF;
-	font-size: small;
-}
-.syndieBlogTopNavUser {
-    text-align: left;
-}
-.syndieBlogTopNavAdmin {
-    text-align: right;
-}
-.syndieBlogHeader {
-    width: 100%;
-    font-size: 1.4em;
-    background-color: #000;
-    text-align: Left;
-    float: Left;
-}
-.syndieBlogHeader a {
-    color: #FFF;
-    padding: 4px;
-}
-.syndieBlogHeader a:hover {
-	color:#88F;
-	padding: 4px;
-}
-.syndieBlogLogo {
-    float: left;
-    display: inline;
-}
-.syndieBlogLinks {
-    width: 20%;
-    float: left;
-}
-.syndieBlogLinkGroup {
-    font-size: 0.8em;
-    background-color: #DDD;
-    border: 1px solid black;
-    margin: 5px;
-    padding: 2px;
-}
-.syndieBlogLinkGroup ul {
-    list-style: none;
-}
-.syndieBlogLinkGroup li {
-}
-.syndieBlogLinkGroupName {
-    font-weight: bold;
-    width: 100%;
-    border-bottom: 1px dashed black;
-    display: block;
-}
-.syndieBlogPostInfoGroup {
-    font-size: 0.8em;
-    background-color: #FFEA9F;
-    border: 1px solid black;
-    margin: 5px;
-    padding: 2px;
-}
-.syndieBlogPostInfoGroup ol {
-   list-style: none;
-}
-.syndieBlogPostInfoGroup li {
-}
-.syndieBlogPostInfoGroup li a {
-    display: block;
-}
-.syndieBlogPostInfoGroupName {
-    font-weight: bold;
-    width: 100%;
-    border-bottom: 1px dashed black;
-    display: block;
-}
-.syndieBlogMeta {
-    text-align: left;
-    font-size: 0.8em;
-    background-color: #DDD;
-    border: 1px solid black;
-    margin: 5px;
-    padding: 2px;
-}
-.syndieBlogBody {
-    width: 80%;
-    float: left;
-}
-.syndieBlogPost {
-    border: 1px solid black;
-    margin-top: 5px;
-    margin-right: 5px;
-}
-.syndieBlogPostHeader {
-    background-color: #FFB;
-    padding: 2px;
-}
-.syndieBlogPostSubject {
-    font-weight: bold;
-}
-.syndieBlogPostFrom {
-    text-align: right;
-}
-.syndieBlogPostSummary {
-    background-color: #FFF;
-    padding: 2px;
-}
-.syndieBlogPostDetails {
-    background-color: #FFC;
-    padding: 2px;
-}
-.syndieBlogNav {
-    text-align: center;
-}
-.syndieBlogComments {
-    border: none;
-    margin-top: 5px;
-    margin-left: 0px;
-    float: left;
-}
-.syndieBlogComments ul {
-    list-style: none;
-    margin-left: 10px;
-}
-.syndieBlogCommentInfoGroup {
-    font-size: 0.8em;
-    margin-right: 5px;
-}
-.syndieBlogCommentInfoGroup ol {
-    list-style: none;
-}
-.syndieBlogCommentInfoGroup li {
-}
-.syndieBlogCommentInfoGroup li a {
-    display: block;
-}
-.syndieBlogCommentInfoGroupName {
-    font-size: 0.8em;
-    font-weight: bold;
-}
-
-.syndieBlogFavorites {
-    float: left;
-    margin: 5px 0px 0 0;
-   display: inline;
-}
-.syndieBlogList {
-    float: right;
-    margin: 5px 0px 0 0;
-   display: inline;
-}
-.b_topnavUser {
-   text-align: right;
-   background-color: #CCD;
-}
-.b_topnavHome {
-   background-color: #CCD;
-   color: #000;
-   width: 50px;
-   text-align: left;
-}
-.b_topnav {
-   background-color: #CCD;
-}
-.b_content {
-}
-.s_summary_overall {
-}
-.s_detail_overall {
-}
-.s_detail_subject {
-    font-size: 0.8em;
-    text-align: left;
-    background-color: #CCF;
-}
-.s_detail_quote {
-    margin-left: 1em;
-    border: 1px solid #DBDBDB;
-    background-color: #E0E0E0;
-}
-.s_detail_italic {
-    font-style: italic;
-}
-.s_detail_bold {
-    font-style: normal;
-    font-weight: bold;
-}
-.s_detail_underline {
-    font-style: normal;
-    text-decoration: underline;
-}
-.s_detail_meta {
-   font-size: 0.8em;
-   text-align: right;
-   background-color: #CCF;
-}
-
-.s_summary_subject {
-   font-size: 0.8em;
-   text-align: left;
-   background-color: #CCF;
-}
-.s_summary_meta {
-   font-size: 0.8em;
-   text-align: right;
-   background-color: #CCF;
-}
-.s_summary_quote {
-    margin-left: 1em;
-    border-width: 1px solid #DBDBDB;
-    background-color: #E0E0E0;
-}
-.s_summary_italic {
-    font-style: italic;
-}
-.s_summary_bold {
-    font-style: normal;
-    font-weight: bold;
-}
-.s_summary_underline {
-    font-style: normal;
-    text-decoration: underline;
-}
-.s_summary_summDetail {
-   font-size: 0.8em;
-}
-.s_detail_summDetail {
-}
-.s_detail_summDetailBlog {
-}
-.s_detail_summDetailBlogLink {
-}
-td.s_detail_summDetail {
-   background-color: #CCF;
-}
-td.s_summary_summ {	width: 80%;
-   font-size: 0.8em;
-   background-color: #CCF;
-}
\ No newline at end of file
diff --git a/apps/syndie/jsp/syndie/index.jsp b/apps/syndie/jsp/syndie/index.jsp
deleted file mode 100644
index 5517346b612057755b733ab74eac42310c18dd2c..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/syndie/index.jsp
+++ /dev/null
@@ -1 +0,0 @@
-<%response.sendRedirect("../index.jsp");%>
\ No newline at end of file
diff --git a/apps/syndie/jsp/viewattachment.jsp b/apps/syndie/jsp/viewattachment.jsp
deleted file mode 100644
index a8e76171b734ffc1880c122e809565f79aaf46db..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/viewattachment.jsp
+++ /dev/null
@@ -1,16 +0,0 @@
-<%@page autoFlush="false" import="net.i2p.syndie.web.*" %><% 
-
-request.setCharacterEncoding("UTF-8"); 
-java.util.Map params = request.getParameterMap();
-response.setContentType(ArchiveViewerBean.getAttachmentContentType(params));
-boolean inline = ArchiveViewerBean.getAttachmentShouldShowInline(params);
-String filename = ArchiveViewerBean.getAttachmentFilename(params);
-if (inline)
-  response.setHeader("Content-Disposition", "inline; filename=\"" + filename + "\"");
-else
-  response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
-int len = ArchiveViewerBean.getAttachmentContentLength(params);
-if (len >= 0)
-  response.setContentLength(len);
-ArchiveViewerBean.renderAttachment(params, response.getOutputStream());
-%>
\ No newline at end of file
diff --git a/apps/syndie/jsp/viewmetadata.jsp b/apps/syndie/jsp/viewmetadata.jsp
deleted file mode 100644
index 9547f40ccad4d7469dd813f08be06d26faa4a6bc..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/viewmetadata.jsp
+++ /dev/null
@@ -1,35 +0,0 @@
-<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.i2p.syndie.web.*, net.i2p.syndie.*" %><% 
-request.setCharacterEncoding("UTF-8"); 
-%><jsp:useBean scope="session" class="net.i2p.syndie.User" id="user" 
-/><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
-<html>
-<head>
-<title>SyndieMedia metadata</title>
-<link href="style.jsp" rel="stylesheet" type="text/css" >
-</head>
-<body>
-<table border="1" cellpadding="0" cellspacing="0" width="100%">
-<tr class="b_toplogo"><td colspan="5" valign="top" align="left" class="b_toplogo"><jsp:include page="_toplogo.jsp" /></td></tr>
-<tr><td valign="top" align="left" rowspan="2" class="b_leftnav"><jsp:include page="_leftnav.jsp" /></td>
-    <jsp:include page="_topnav.jsp" />
-    <td valign="top" align="left" rowspan="2" class="b_rightnav"><jsp:include page="_rightnav.jsp" /></td></tr>
-<tr class="b_content"><td valign="top" align="left" colspan="3" class="b_content"><%
-ArchiveViewerBean.renderMetadata(user, request.getRequestURI(), request.getParameterMap(), out); 
-if (user.getAuthenticated()) {
-  if ("Authorize".equals(request.getParameter("action"))) {
-    %><span class="b_metaStatus"><%=BlogManager.instance().authorizeRemoteAccess(user, request.getParameter("password"))%></span><%
-  }
-  if (!user.getAllowAccessRemote()) { 
-    if (user.getBlog().toBase64().equals(request.getParameter("blog"))) {
-  %><hr /><form action="viewmetadata.jsp" method="POST">
-<input type="hidden" name="blog" value="<%=request.getParameter("blog")%>" />
-<span class="b_metaAuthorize">To access remote instances from this instance, please supply the Syndie administration password:</span> 
-<input class="b_metaAuthorize" type="password" name="password" />
-<input class="b_metaAuthorizeSubmit" type="submit" name="action" value="Authorize" />
-</form><%
-    }
-  }
-}
-%></td></tr>
-</table>
-</body>
\ No newline at end of file
diff --git a/apps/syndie/jsp/viewtempattachment.jsp b/apps/syndie/jsp/viewtempattachment.jsp
deleted file mode 100644
index 0eae918f99fc6a9be4b60a017c22503226acbc17..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/viewtempattachment.jsp
+++ /dev/null
@@ -1,23 +0,0 @@
-<%@page  import="net.i2p.syndie.web.ArchiveViewerBean" %><jsp:useBean 
-scope="session" class="net.i2p.syndie.web.PostBean" id="post" /><%
-request.setCharacterEncoding("UTF-8");
-java.util.Map params = request.getParameterMap();
-String id = request.getParameter(ArchiveViewerBean.PARAM_ATTACHMENT);
-if (id != null) {
-  try {
-    int attachmentId = Integer.parseInt(id);
-    if ( (attachmentId < 0) || (attachmentId >= post.getAttachmentCount()) ) {
-      %>Attachment <%=attachmentId%> does not exist<%
-    } else {
-      response.setContentType(post.getContentType(attachmentId));
-      boolean inline = ArchiveViewerBean.getAttachmentShouldShowInline(params);
-      String filename = ArchiveViewerBean.getAttachmentFilename(params);
-      if (inline)
-        response.setHeader("Content-Disposition", "inline; filename=" + filename);
-      else
-        response.setHeader("Content-Disposition", "attachment; filename=" + filename);
-      post.writeAttachmentData(attachmentId, response.getOutputStream());
-    }
-  } catch (NumberFormatException nfe) {}
-}
-%>
\ No newline at end of file
diff --git a/apps/syndie/jsp/web.xml b/apps/syndie/jsp/web.xml
deleted file mode 100644
index 3da3b8bd36702581d29bb73651a4f9cbdf6190df..0000000000000000000000000000000000000000
--- a/apps/syndie/jsp/web.xml
+++ /dev/null
@@ -1,166 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<!DOCTYPE web-app
-    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
-    "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
-
-<web-app>
-    <servlet>
-     <servlet-name>net.i2p.syndie.web.ArchiveServlet</servlet-name>
-     <servlet-class>net.i2p.syndie.web.ArchiveServlet</servlet-class>
-    </servlet>
-     
-    <servlet>
-     <servlet-name>net.i2p.syndie.web.RSSServlet</servlet-name>
-     <servlet-class>net.i2p.syndie.web.RSSServlet</servlet-class>
-    </servlet>
-     
-    <servlet>
-     <servlet-name>net.i2p.syndie.web.ViewThreadedServlet</servlet-name>
-     <servlet-class>net.i2p.syndie.web.ViewThreadedServlet</servlet-class>
-    </servlet>
-    
-    <servlet>
-     <servlet-name>net.i2p.syndie.web.ProfileServlet</servlet-name>
-     <servlet-class>net.i2p.syndie.web.ProfileServlet</servlet-class>
-    </servlet>
-     
-    <servlet>
-     <servlet-name>net.i2p.syndie.web.SwitchServlet</servlet-name>
-     <servlet-class>net.i2p.syndie.web.SwitchServlet</servlet-class>
-    </servlet>
-     
-    <servlet>
-     <servlet-name>net.i2p.syndie.web.AddressesServlet</servlet-name>
-     <servlet-class>net.i2p.syndie.web.AddressesServlet</servlet-class>
-    </servlet>
-     
-    <servlet>
-     <servlet-name>net.i2p.syndie.web.PostServlet</servlet-name>
-     <servlet-class>net.i2p.syndie.web.PostServlet</servlet-class>
-    </servlet>
-     
-    <servlet>
-     <servlet-name>net.i2p.syndie.web.AdminServlet</servlet-name>
-     <servlet-class>net.i2p.syndie.web.AdminServlet</servlet-class>
-    </servlet>
-     
-    <servlet>
-     <servlet-name>net.i2p.syndie.web.SyndicateServlet</servlet-name>
-     <servlet-class>net.i2p.syndie.web.SyndicateServlet</servlet-class>
-    </servlet>
-     
-    <servlet>
-     <servlet-name>net.i2p.syndie.web.ImportFeedServlet</servlet-name>
-     <servlet-class>net.i2p.syndie.web.ImportFeedServlet</servlet-class>
-    </servlet>
-     
-    <servlet>
-     <servlet-name>net.i2p.syndie.web.ExternalLinkServlet</servlet-name>
-     <servlet-class>net.i2p.syndie.web.ExternalLinkServlet</servlet-class>
-    </servlet>
-     
-    <servlet>
-     <servlet-name>net.i2p.syndie.web.ThreadNavServlet</servlet-name>
-     <servlet-class>net.i2p.syndie.web.ThreadNavServlet</servlet-class>
-    </servlet>
-     
-    <servlet>
-     <servlet-name>net.i2p.syndie.web.ViewBlogsServlet</servlet-name>
-     <servlet-class>net.i2p.syndie.web.ViewBlogsServlet</servlet-class>
-    </servlet>
-     
-    <servlet>
-     <servlet-name>net.i2p.syndie.web.BlogConfigServlet</servlet-name>
-     <servlet-class>net.i2p.syndie.web.BlogConfigServlet</servlet-class>
-    </servlet>
-     
-    <servlet>
-     <servlet-name>net.i2p.syndie.web.ViewBlogServlet</servlet-name>
-     <servlet-class>net.i2p.syndie.web.ViewBlogServlet</servlet-class>
-    </servlet>
-     
-    <servlet>
-	 <servlet-name>net.i2p.syndie.UpdaterServlet</servlet-name>
-	 <servlet-class>net.i2p.syndie.UpdaterServlet</servlet-class>
-         <load-on-startup>1</load-on-startup>
-    </servlet>
-     
-    <!-- precompiled servlets -->
-    
-    <!--
-    <servlet-mapping> 
-      <servlet-name>net.i2p.syndie.jsp.index_jsp</servlet-name>
-      <url-pattern>/</url-pattern>
-    </servlet-mapping>
-    -->
-    <servlet-mapping> 
-      <servlet-name>net.i2p.syndie.web.ArchiveServlet</servlet-name>
-      <url-pattern>/archive/*</url-pattern>
-    </servlet-mapping>
-    <servlet-mapping> 
-      <servlet-name>net.i2p.syndie.web.RSSServlet</servlet-name>
-      <url-pattern>/rss.jsp</url-pattern>
-    </servlet-mapping>
-    <servlet-mapping> 
-      <servlet-name>net.i2p.syndie.web.ViewThreadedServlet</servlet-name>
-      <url-pattern>/threads.jsp</url-pattern>
-    </servlet-mapping>
-    <servlet-mapping> 
-      <servlet-name>net.i2p.syndie.web.ProfileServlet</servlet-name>
-      <url-pattern>/profile.jsp</url-pattern>
-    </servlet-mapping>
-    <servlet-mapping> 
-      <servlet-name>net.i2p.syndie.web.SwitchServlet</servlet-name>
-      <url-pattern>/switchuser.jsp</url-pattern>
-    </servlet-mapping>
-    <servlet-mapping> 
-      <servlet-name>net.i2p.syndie.web.AddressesServlet</servlet-name>
-      <url-pattern>/addresses.jsp</url-pattern>
-    </servlet-mapping>
-    <servlet-mapping> 
-      <servlet-name>net.i2p.syndie.web.PostServlet</servlet-name>
-      <url-pattern>/post.jsp</url-pattern>
-    </servlet-mapping>
-    <servlet-mapping> 
-      <servlet-name>net.i2p.syndie.web.AdminServlet</servlet-name>
-      <url-pattern>/admin.jsp</url-pattern>
-    </servlet-mapping>
-    <servlet-mapping> 
-      <servlet-name>net.i2p.syndie.web.SyndicateServlet</servlet-name>
-      <url-pattern>/syndicate.jsp</url-pattern>
-    </servlet-mapping>
-    <servlet-mapping> 
-      <servlet-name>net.i2p.syndie.web.ImportFeedServlet</servlet-name>
-      <url-pattern>/importfeed.jsp</url-pattern>
-    </servlet-mapping>
-    <servlet-mapping> 
-      <servlet-name>net.i2p.syndie.web.ExternalLinkServlet</servlet-name>
-      <url-pattern>/externallink.jsp</url-pattern>
-    </servlet-mapping>
-    <servlet-mapping> 
-      <servlet-name>net.i2p.syndie.web.ThreadNavServlet</servlet-name>
-      <url-pattern>/threadnav/*</url-pattern>
-    </servlet-mapping>
-    <servlet-mapping> 
-      <servlet-name>net.i2p.syndie.web.ViewBlogsServlet</servlet-name>
-      <url-pattern>/blogs.jsp</url-pattern>
-    </servlet-mapping>
-    <servlet-mapping> 
-      <servlet-name>net.i2p.syndie.web.BlogConfigServlet</servlet-name>
-      <url-pattern>/configblog.jsp</url-pattern>
-    </servlet-mapping>
-    <servlet-mapping> 
-      <servlet-name>net.i2p.syndie.web.ViewBlogServlet</servlet-name>
-      <url-pattern>/blog.jsp</url-pattern>
-    </servlet-mapping>
-    
-    <session-config>
-        <session-timeout>
-            30
-        </session-timeout>
-    </session-config>
-    <welcome-file-list>
-        <welcome-file>index.html</welcome-file>
-        <welcome-file>index.jsp</welcome-file>
-    </welcome-file-list>
-</web-app>
diff --git a/history.txt b/history.txt
index e997f5938d2593487d4b99245375ca9906e35b83..cafd58404cae0096bc2489ed1f0fcbebc2077a91 100644
--- a/history.txt
+++ b/history.txt
@@ -1,3 +1,79 @@
+2009-01-03 zzz
+    * config.jsp: Move the buttons up
+    * configservice.jsp: Clean up and fix the broken (?)
+      browser launch configuration
+    * i2psnark:
+      - Try again to remove the i2psnarkurl files on shutdown
+      - Sort torrents with a locale-based sort
+    * NetDb:
+      - Expire routers with introducers after 90m.
+        This should improve reachability to firewalled routers
+        by keeping introducer info current.
+      - Expire routers with no addresses after 90m.
+      - Convert to java concurrent
+    * Stats: Add router.memoryUsed, graph by default
+    * Summary bar: Remove spurious UDP warning on startup
+    * UpdateHandler: Make extensible for upcoming
+      torrent updater
+
+2008-12-15 zzz
+    * Remove apps/ bogobot jdom pants q rome stasher syndie
+
+2008-12-14 zzz
+    * Contexts: Add int getProperty(String prop, int default)
+    * I2PAppThread: Constructor fix
+    * More split classes into their own files for mkvore
+    * Streaming: Don't build test cases by default
+    * Summary bar: Replace links with buttons
+    * Transport:
+      - Cleanup max connections code
+      - Add i2np.udp.maxConnections
+      - Set max connections based on share bandwidth
+      - Add haveCapacity() that can be used for connection
+        throttling in the router
+      - Reject IBGW/OBEP requests when near connection limit
+      - Reduce idle timeout when near connection limit
+    * Tunnel request handler:
+      - Require tunnel.dropLoad* stats
+      - Speed up request loop
+    * I2CP, HostsTxtNamingService, I2PTunnel:
+      Implement Base32 Hash hostnames, via the naming service.
+      Names are of the form [52-characters].i2p, where
+      the 52 characters are the Base32 representation of our
+      256-byte hash. The client requests a lookup of the hash
+      via a brief I2CP session using new I2CP request/reply
+      messages. The router looks up the leaseset for the hash
+      to convert the hash to a dest. Convert the I2PTunnel
+      'preview' links to use Base32 hostnames as a
+      demonstration.
+
+2008-12-08 zzz
+    * ATalk: Move from core to apps
+    * Blocklists: enable by default, include blocklist file
+      in new installs
+    * Build: Add findbugs target
+    * Cleanup of removed netdb stats
+    * Console:
+      - Don't display restart button if no wrapper
+      - Remove PRNG stats
+    * Eepsite: Disable jetty webapps by default for new installs
+    * i2psnark:
+      - Add default i2psnark.config for new installs
+      - Remove wishlist link
+      - Recognize robert and i2psnarkxl clients
+      - Increase max files to 256
+    * Increase standalone heap size to 128MB
+    * NetDb: Split classes into their own files for mkvore
+    * PeerManager: Fix NPE on early shutdown
+    * SusiDNS: Add textareas
+    * Transport:
+      - Fixes, avoid NPEs, and cleanups when NTCP and/or UDP transports disabled
+      - More TCP removal cleanup
+      - Clean up bandwidth limiting, centralize defaults
+      - Force burst to be >= limit
+      - Increase default bw to 48/24, burst 64/32
+    * Tunnels: Avoid two NPEs on corrupt fragments
+
 2008-12-01 zzz
     * i2psnark:
       - Refactor to allow running a single Snark without a SnarkManager again,
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index bca9500ce7a6e63fd140883c3f9bb1fc0f8632df..55eaad0f3bdd7500828b919715e293f544a0fc06 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -17,7 +17,7 @@ import net.i2p.CoreVersion;
 public class RouterVersion {
     public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $";
     public final static String VERSION = "0.6.5";
-    public final static long BUILD = 1;
+    public final static long BUILD = 8;
     public static void main(String args[]) {
         System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
         System.out.println("Router ID: " + RouterVersion.ID);