diff --git a/apps/BOB/src/net/i2p/BOB/BOB.java b/apps/BOB/src/net/i2p/BOB/BOB.java
index ca2d6f878a67c2a5e871c26df871176e1370f680..cd4ccfdcb46d9bb2a31da9f5e904d47c552735b4 100644
--- a/apps/BOB/src/net/i2p/BOB/BOB.java
+++ b/apps/BOB/src/net/i2p/BOB/BOB.java
@@ -113,7 +113,7 @@ public class BOB {
 	public final static String PROP_BOB_PORT = "BOB.port";
 	public final static String PROP_BOB_HOST = "BOB.host";
 	private static int maxConnections = 0;
-	private static nickname database;
+	private static NamedDB database;
 
 	/**
 	 * Log a warning
@@ -141,7 +141,7 @@ public class BOB {
 	 * @param args
 	 */
 	public static void main(String[] args) {
-		database = new nickname();
+		database = new NamedDB();
 		int i = 0;
 		boolean save = false;
 		// Set up all defaults to be passed forward to other threads.
@@ -212,10 +212,10 @@ public class BOB {
 			Socket server;
 
 			while((i++ < maxConnections) || (maxConnections == 0)) {
-				//doCMDS connection;
+				//DoCMDS connection;
 
 				server = listener.accept();
-				doCMDS conn_c = new doCMDS(server, props, database, _log);
+				DoCMDS conn_c = new DoCMDS(server, props, database, _log);
 				Thread t = new Thread(conn_c);
 				t.start();
 			}
diff --git a/apps/BOB/src/net/i2p/BOB/DoCMDS.java b/apps/BOB/src/net/i2p/BOB/DoCMDS.java
new file mode 100644
index 0000000000000000000000000000000000000000..f309bcb96d9135cbf726dc39025e02e2adece8d0
--- /dev/null
+++ b/apps/BOB/src/net/i2p/BOB/DoCMDS.java
@@ -0,0 +1,1349 @@
+/**
+ *            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.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.net.Socket;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import net.i2p.I2PException;
+import net.i2p.client.I2PClientFactory;
+import net.i2p.data.Destination;
+import net.i2p.util.Log;
+
+/**
+ * Simplistic command parser for BOB
+ *
+ * @author sponge
+ *
+ */
+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 = "-C";
+	public static final String BOBversion = BMAJ + "." + BMIN + "." + BREV + BEXT;
+	private Socket server;
+	private Properties props;
+	private NamedDB database;
+	private String line;
+	private Destination d;
+	private ByteArrayOutputStream prikey;
+	private boolean dk,  ns,  ip,  op;
+	private NamedDB nickinfo;
+	private Log _log;
+	/* database strings */
+	private static final String P_DEST = "DESTINATION";
+	private static final String P_INHOST = "INHOST";
+	private static final String P_INPORT = "INPORT";
+	private static final String P_KEYS = "KEYS";
+	private static final String P_NICKNAME = "NICKNAME";
+	private static final String P_OUTHOST = "OUTHOST";
+	private static final String P_OUTPORT = "OUTPORT";
+	private static final String P_PROPERTIES = "PROPERTIES";
+	private static final String P_QUIET = "QUIET";
+	private static final String P_RUNNING = "RUNNING";
+	private static final String P_STARTING = "STARTING";
+	private static final String P_STOPPING = "STOPPING";
+
+	/* command strings */
+	private static final String C_help = "help";
+	private static final String C_clear = "clear";
+	private static final String C_getdest = "getdest";
+	private static final String C_getkeys = "getkeys";
+	private static final String C_getnick = "getnick";
+	private static final String C_inhost = "inhost";
+	private static final String C_inport = "inport";
+	private static final String C_list = "list";
+	private static final String C_newkeys = "newkeys";
+	private static final String C_option = "option";
+	private static final String C_outhost = "outhost";
+	private static final String C_outport = "outport";
+	private static final String C_quiet = "quiet";
+	private static final String C_quit = "quit";
+	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_start = "start";
+	private static final String C_status = "status";
+	private static final String C_stop = "stop";
+
+	/* all the coomands available, plus description */
+	private static final String C_ALL[][] = {
+		{C_help, C_help + " <command> * Get help on a command."},
+		{C_clear, C_clear + " * Clear the current nickname out of the list."},
+		{C_getdest, C_getdest + " * Return the destination for the current nickname."},
+		{C_getkeys, C_getkeys + " * Return the keypair for the current nickname."},
+		{C_getnick, C_getnick + " tunnelname * Set the nickname from the database."},
+		{C_inhost, C_inhost + " hostname | IP * Set the inbound hostname or IP."},
+		{C_inport, C_inport + " port_number * Set the inbound port number nickname listens on."},
+		{C_list, C_list + " * List all tunnels."},
+		{C_newkeys, C_newkeys + " * Generate a new keypair for the current nickname."},
+		{C_option, C_option + " I2CPoption=something * Set an I2CP option. NOTE: Don't use any spaces."},
+		{C_outhost, C_outhost + " hostname | IP * Set the outbound hostname or IP."},
+		{C_outport, C_outport + " port_number * Set the outbound port that nickname contacts."},
+		{C_quiet, C_quiet + " True | False * Don't send to the application the incoming destination."},
+		{C_quit, C_quit + " * Quits this session with BOB."},
+		{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_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."},
+		{"", "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
+		},
+		{" ", " "} // end of list
+	};
+
+	/**
+	 *
+	 * @param server
+	 * @param props
+	 * @param database
+	 * @param _log
+	 */
+	DoCMDS(Socket server, Properties props, NamedDB database, Log _log) {
+		this.server = server;
+		this.props = new Properties(props);
+		this.database = database;
+		this._log = _log;
+	}
+
+	private void rlock() throws Exception {
+		rlock(nickinfo);
+	}
+
+	private void rlock(NamedDB Arg) throws Exception {
+		database.getReadLock();
+		Arg.getReadLock();
+	}
+
+	private void runlock() throws Exception {
+		runlock(nickinfo);
+	}
+
+	private void runlock(NamedDB Arg) throws Exception {
+		Arg.releaseReadLock();
+		database.releaseReadLock();
+	}
+
+	private void wlock() throws Exception {
+		wlock(nickinfo);
+	}
+
+	private void wlock(NamedDB Arg) throws Exception {
+		database.getWriteLock();
+		Arg.getWriteLock();
+	}
+
+	private void wunlock() throws Exception {
+		wunlock(nickinfo);
+	}
+
+	private void wunlock(NamedDB Arg) throws Exception {
+		Arg.releaseWriteLock();
+		database.releaseWriteLock();
+	}
+
+	/**
+	 * Try to print info from the database
+	 *
+	 * @param out
+	 * @param info
+	 * @param key
+	 * @throws Exception
+	 */
+	private void trypnt(PrintStream out, NamedDB info, Object key) throws Exception {
+		try {
+			rlock(info);
+		} catch(Exception e) {
+			throw new Exception(e);
+		}
+		try {
+			out.print(" " + key + ": ");
+			if(info.exists(key)) {
+				out.print(info.get(key));
+			} else {
+				out.print("not_set");
+			}
+		} catch(Exception e) {
+			runlock(info);
+			throw new Exception(e);
+		}
+		runlock(info);
+	}
+
+	/**
+	 * Print true or false if an object exists
+	 *
+	 * @param out
+	 * @param info
+	 * @param key
+	 * @throws Exception
+	 */
+	private void tfpnt(PrintStream out, NamedDB info, Object key) throws Exception {
+		try {
+			rlock(info);
+		} catch(Exception e) {
+			throw new Exception(e);
+		}
+		try {
+			out.print(" " + key + ": ");
+			out.print(info.exists(key));
+		} catch(Exception e) {
+			runlock(info);
+			throw new Exception(e);
+		}
+		runlock(info);
+	}
+
+	/**
+	 * Print an error message
+	 *
+	 * @param out
+	 */
+	private void nns(PrintStream out) throws IOException {
+		out.println("ERROR no nickname has been set");
+	}
+
+	/**
+	 * Dump various information from the database
+	 *
+	 * @param out
+	 * @param info
+	 * @throws Exception
+	 */
+	private void nickprint(PrintStream out, NamedDB info) throws Exception {
+		try {
+			rlock(info);
+		} catch(Exception e) {
+			throw new Exception(e);
+		}
+		try {
+
+			trypnt(out, info, P_NICKNAME);
+			trypnt(out, info, P_STARTING);
+			trypnt(out, info, P_RUNNING);
+			trypnt(out, info, P_STOPPING);
+			tfpnt(out, info, P_KEYS);
+			trypnt(out, info, P_QUIET);
+			trypnt(out, info, P_INPORT);
+			trypnt(out, info, P_INHOST);
+			trypnt(out, info, P_OUTPORT);
+			trypnt(out, info, P_OUTHOST);
+			out.println();
+		} catch(Exception e) {
+			runlock(info);
+			throw new Exception(e);
+		}
+
+		runlock(info);
+	}
+
+	/**
+	 * Print information on a specific record, indicated by NamedDB
+	 * @param out
+	 * @param Arg
+	 * @throws Exception
+	 */
+	private void ttlpnt(PrintStream out, Object Arg) throws Exception {
+		try {
+			database.getReadLock();
+		} catch(Exception e) {
+			throw new Exception(e);
+		}
+
+		try {
+			if(database.exists(Arg)) {
+				out.print("DATA");
+				nickprint(out, (NamedDB)database.get(Arg));
+			}
+		} catch(Exception e) {
+			database.releaseReadLock();
+			throw new Exception(e);
+		}
+
+
+		database.releaseReadLock();
+	}
+
+	/**
+	 * Is this NamedDB's tunnel active?
+	 *
+	 * @param Arg
+	 * @return true if the tunnel is active
+	 */
+	private boolean tunnelactive(NamedDB Arg) throws Exception {
+		boolean retval;
+		try {
+			rlock(Arg);
+		} catch(Exception e) {
+			throw new Exception(e);
+		}
+
+		try {
+			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) {
+			runlock();
+			throw new Exception(e);
+		}
+
+		runlock(Arg);
+		return retval;
+	}
+
+	/**
+	 * Does the base64 information look OK
+	 *
+	 * @param data
+	 * @return
+	 */
+	private boolean is64ok(String data) {
+		String dest = new String(data);
+		if(dest.replaceAll("[a-zA-Z0-9~-]", "").length() == 0) {
+			return true;
+		}
+
+		return false;
+	}
+
+	/**
+	 * The actual parser.
+	 * It probabbly needs a rewrite into functions, but I kind-of like inline code.
+	 *
+	 */
+	public void run() {
+		dk = ns = ip = op = false;
+
+		try {
+			// Get input from the client
+			BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));
+			PrintStream out = new PrintStream(server.getOutputStream());
+quit:                   {
+die:                            {
+					prikey = new ByteArrayOutputStream();
+					out.println("BOB " + BOBversion);
+					out.println("OK");
+					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) {
+							Command = token.nextToken();
+							Command =
+								Command.toLowerCase();
+							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)) {
+										out.println("OK " + C_ALL[i][1]);
+									}
+
+								}
+							} else if(Command.equals(C_getdest)) {
+								if(ns) {
+									if(dk) {
+										try {
+											rlock();
+										} catch(Exception ex) {
+											break die;
+										}
+
+										try {
+											out.println("OK " + nickinfo.get(P_DEST));
+										} catch(Exception e) {
+											try {
+												runlock();
+											} catch(Exception ex) {
+												break die;
+											}
+											break die;
+										}
+
+										try {
+											runlock();
+										} catch(Exception ex) {
+											break die;
+										}
+
+									} else {
+										out.println("ERROR keys not set.");
+									}
+
+								} else {
+									nns(out);
+								}
+
+							} else if(Command.equals(C_list)) {
+								// Produce a formatted list of all nicknames
+								database.getReadLock();
+								for(int i = 0; i <
+									database.getcount(); i++) {
+									try {
+										info = (NamedDB)database.getnext(i);
+										out.print("DATA");
+									} catch(Exception e) {
+										database.releaseReadLock();
+										break die;
+									}
+
+									try {
+										info.getReadLock();
+									} catch(Exception ex) {
+										break die;
+									}
+									try {
+										nickprint(out, info);
+									} catch(Exception e) {
+										try {
+											info.releaseReadLock();
+											database.releaseReadLock();
+										} catch(Exception ex) {
+											break die;
+										}
+										break die;
+									}
+
+									try {
+										info.releaseReadLock();
+									} catch(Exception ex) {
+										break die;
+									}
+								}
+
+								try {
+									database.releaseReadLock();
+								} catch(Exception ex) {
+									break die;
+								}
+								out.println("OK Listing done");
+							} else if(Command.equals(C_quit)) {
+								// End the command session
+								break quit;
+							} else if(Command.equals(C_newkeys)) {
+								if(ns) {
+									try {
+										if(tunnelactive(nickinfo)) {
+											out.println("ERROR tunnel is active");
+										} else {
+											try {
+												// Make a new PublicKey and PrivateKey
+												prikey = new ByteArrayOutputStream();
+												d = I2PClientFactory.createClient().createDestination(prikey);
+												try {
+													wlock();
+												} catch(Exception e) {
+													break die;
+												}
+
+												try {
+													nickinfo.add(P_KEYS, prikey.toByteArray());
+													nickinfo.add(P_DEST, d.toBase64());
+												} catch(Exception e) {
+													try {
+														wunlock();
+													} catch(Exception ex) {
+														break die;
+													}
+													break die;
+												}
+
+												dk = true;
+												try {
+													wunlock();
+												} catch(Exception ex) {
+													break die;
+												}
+
+												try {
+													rlock();
+												} catch(Exception ex) {
+													break die;
+												}
+
+												try {
+													out.println("OK " + nickinfo.get(P_DEST));
+												} catch(Exception e) {
+													runlock();
+													break die;
+												}
+
+												try {
+													runlock();
+												} catch(Exception ex) {
+													break die;
+												}
+											} catch(I2PException ipe) {
+												BOB.error("Error generating keys" + ipe);
+												out.println("ERROR generating keys");
+											}
+
+										}
+									} catch(Exception e) {
+										break die;
+									}
+
+								} else {
+									try {
+										nns(out);
+									} catch(Exception ex) {
+										break die;
+									}
+								}
+
+							} else if(Command.equals(C_getkeys)) {
+								// Return public key
+								if(dk) {
+									prikey = new ByteArrayOutputStream();
+									try {
+										rlock();
+									} catch(Exception e) {
+										break die;
+									}
+									try {
+										prikey.write(((byte[])nickinfo.get(P_KEYS)));
+									} catch(Exception ex) {
+										try {
+											runlock();
+										} catch(Exception ee) {
+											break die;
+										}
+										break die;
+									}
+									try {
+										runlock();
+									} catch(Exception e) {
+										break die;
+									}
+
+									out.println("OK " + net.i2p.data.Base64.encode(prikey.toByteArray()));
+								} else {
+									out.println("ERROR no public key has been set");
+								}
+
+							} else if(Command.equals(C_quiet)) {
+								if(ns) {
+									try {
+										if(tunnelactive(nickinfo)) {
+											out.println("ERROR tunnel is active");
+										} else {
+											try {
+												wlock();
+											} catch(Exception ex) {
+												break die;
+											}
+											try {
+												nickinfo.add(P_QUIET, new Boolean(Boolean.parseBoolean(Arg) == true));
+											} catch(Exception ex) {
+												try {
+													wunlock();
+												} catch(Exception ee) {
+													break die;
+												}
+												break die;
+											}
+
+											try {
+												wunlock();
+											} catch(Exception ex) {
+												break die;
+											}
+
+											out.println("OK Quiet set");
+										}
+
+									} catch(Exception ex) {
+										break die;
+									}
+
+								} else {
+									try {
+										nns(out);
+									} catch(Exception ex) {
+										break die;
+									}
+								}
+
+							} else if(Command.equals(C_setkeys)) {
+								// Set the NamedDB to a privatekey in BASE64 format
+								if(ns) {
+									try {
+										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) {
+												Arg = "";
+											}
+
+											if((Arg.length() == 884) && is64ok(Arg)) {
+												try {
+													wlock();
+												} catch(Exception ex) {
+													break die;
+												}
+												try {
+													nickinfo.add(P_KEYS, prikey.toByteArray());
+													nickinfo.add(P_DEST, d.toBase64());
+												} catch(Exception ex) {
+													try {
+														wunlock();
+													} catch(Exception ee) {
+														break die;
+													}
+													break die;
+												}
+												dk = true;
+												try {
+													wunlock();
+												} catch(Exception ex) {
+													break die;
+												}
+
+												try {
+													rlock();
+												} catch(Exception ex) {
+													break die;
+												}
+
+												try {
+													out.println("OK " + nickinfo.get(P_DEST));
+												} catch(Exception e) {
+													try {
+														runlock();
+													} catch(Exception ex) {
+														break die;
+													}
+													break die;
+												}
+
+												try {
+													runlock();
+												} catch(Exception ex) {
+													break die;
+												}
+											} else {
+												out.println("ERROR not in BASE64 format");
+											}
+
+										}
+									} catch(Exception ex) {
+										break die;
+									}
+
+								} else {
+									try {
+										nns(out);
+									} catch(Exception ex) {
+										break die;
+									}
+								}
+
+							} else if(Command.equals(C_setnick)) {
+								ns = dk = ip = op = false;
+								try {
+									database.getReadLock();
+								} catch(Exception ex) {
+									break die;
+								}
+								try {
+									nickinfo = (NamedDB)database.get(Arg);
+									if(!tunnelactive(nickinfo)) {
+										nickinfo = null;
+										ns =
+											true;
+									}
+
+								} catch(Exception b) {
+									nickinfo = null;
+									ns =
+										true;
+								}
+
+								try {
+									database.releaseReadLock();
+								} catch(Exception ex) {
+									break die;
+								}
+								// Clears and Sets the initial NamedDB structure to work with
+								if(ns) {
+									nickinfo = new NamedDB();
+									try {
+										wlock();
+									} catch(Exception e) {
+										break die;
+									}
+
+									try {
+										database.add(Arg, nickinfo);
+										nickinfo.add(P_NICKNAME, Arg);
+										nickinfo.add(P_STARTING, Boolean.FALSE);
+										nickinfo.add(P_RUNNING, Boolean.FALSE);
+										nickinfo.add(P_STOPPING, Boolean.FALSE);
+										nickinfo.add(P_QUIET, Boolean.FALSE);
+										nickinfo.add(P_INHOST, "localhost");
+										nickinfo.add(P_OUTHOST, "localhost");
+										Properties Q = new Properties(props);
+										Q.setProperty("inbound.nickname", Arg);
+										Q.setProperty("outbound.nickname", Arg);
+										nickinfo.add(P_PROPERTIES, Q);
+									} catch(Exception e) {
+										try {
+											wunlock();
+											break die;
+										} catch(Exception ee) {
+											break die;
+										}
+
+									}
+									try {
+										wunlock();
+									} catch(Exception e) {
+										break die;
+									}
+
+									out.println("OK Nickname set to " + Arg);
+								} else {
+									out.println("ERROR tunnel is active");
+								}
+
+							} else if(Command.equals(C_option)) {
+								if(ns) {
+									try {
+										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) {
+												out.println("ERROR to many or no options.");
+											} else {
+												String pname = otoken.nextToken();
+												String pval = otoken.nextToken();
+												try {
+													rlock();
+												} catch(Exception ex) {
+													break die;
+												}
+
+												Properties Q = (Properties)nickinfo.get(P_PROPERTIES);
+												try {
+													runlock();
+												} catch(Exception ex) {
+													break die;
+												}
+
+												Q.setProperty(pname, pval);
+												try {
+													wlock();
+												} catch(Exception ex) {
+													break die;
+												}
+
+												try {
+													nickinfo.add(P_PROPERTIES, Q);
+												} catch(Exception ex) {
+													try {
+														wunlock();
+													} catch(Exception ee) {
+														break die;
+													}
+													break die;
+												}
+												try {
+													wunlock();
+												} catch(Exception ex) {
+													break die;
+												}
+
+												out.println("OK " + pname + " set to " + pval);
+											}
+
+										}
+									} catch(Exception ex) {
+										break die;
+									}
+
+								} else {
+									nns(out);
+								}
+
+							} else if(Command.equals(C_getnick)) {
+								// Get the NamedDB to work with...
+								try {
+									database.getReadLock();
+								} catch(Exception ex) {
+									break die;
+								}
+								try {
+									nickinfo = (NamedDB)database.get(Arg);
+									ns = true;
+								} catch(RuntimeException b) {
+									try {
+										nns(out);
+									} catch(Exception ex) {
+										try {
+											database.releaseReadLock();
+										} catch(Exception ee) {
+											break die;
+										}
+										break die;
+									}
+								}
+
+								database.releaseReadLock();
+								if(ns) {
+									try {
+										rlock();
+									} catch(Exception e) {
+										break die;
+									}
+									try {
+										dk = nickinfo.exists(P_KEYS);
+										ip = nickinfo.exists(P_INPORT);
+										op = nickinfo.exists(P_OUTPORT);
+									} catch(Exception ex) {
+										try {
+											runlock();
+										} catch(Exception ee) {
+											break die;
+										}
+										break die;
+									}
+									try {
+										runlock();
+									} catch(Exception e) {
+										break die;
+									}
+// Finally say OK.
+									out.println("OK Nickname set to " + Arg);
+								}
+
+							} else if(Command.equals(C_inport)) {
+								// Set the NamedDB inbound TO the router port
+								// app --> BOB
+								if(ns) {
+									try {
+										if(tunnelactive(nickinfo)) {
+											out.println("ERROR tunnel is active");
+										} else {
+											int prt;
+											try {
+												wlock();
+											} catch(Exception ex) {
+												break die;
+											}
+
+											try {
+												nickinfo.kill(P_INPORT);
+											} catch(Exception ex) {
+												try {
+													wunlock();
+												} catch(Exception ee) {
+													break die;
+												}
+
+												break die;
+											}
+											try {
+												prt = Integer.parseInt(Arg);
+												if(prt > 1 && prt < 65536) {
+													try {
+														nickinfo.add(P_INPORT, new Integer(prt));
+													} catch(Exception ex) {
+														try {
+															wunlock();
+														} catch(Exception ee) {
+															break die;
+														}
+
+														break die;
+													}
+												}
+
+											} catch(NumberFormatException nfe) {
+												out.println("ERROR not a number");
+											}
+
+											try {
+												wunlock();
+											} catch(Exception ex) {
+												break die;
+											}
+											try {
+												rlock();
+											} catch(Exception ex) {
+												break die;
+											}
+
+											try {
+												ip = nickinfo.exists(P_INPORT);
+											} catch(Exception ex) {
+												try {
+													runlock();
+												} catch(Exception ee) {
+													break die;
+												}
+												break die;
+											}
+											try {
+												runlock();
+											} catch(Exception ex) {
+												break die;
+											}
+
+											if(ip) {
+												out.println("OK inbound port set");
+											} else {
+												out.println("ERROR port out of range");
+											}
+
+										}
+									} catch(Exception ex) {
+										break die;
+									}
+
+								} else {
+									nns(out);
+								}
+
+							} else if(Command.equals(C_outport)) {
+								// Set the NamedDB outbound FROM the router port
+								// BOB --> app
+								if(ns) {
+									try {
+										if(tunnelactive(nickinfo)) {
+											out.println("ERROR tunnel is active");
+										} else {
+											int prt;
+											try {
+												wlock();
+											} catch(Exception ex) {
+												break die;
+											}
+
+											try {
+												nickinfo.kill(P_OUTPORT);
+											} catch(Exception ex) {
+												try {
+													wunlock();
+												} catch(Exception ee) {
+													break die;
+												}
+												break die;
+											}
+											try {
+												prt = Integer.parseInt(Arg);
+												if(prt > 1 && prt < 65536) {
+													try {
+														nickinfo.add(P_OUTPORT, new Integer(prt));
+													} catch(Exception ex) {
+														try {
+															wunlock();
+														} catch(Exception ee) {
+															break die;
+														}
+														break die;
+													}
+												}
+
+											} catch(NumberFormatException nfe) {
+												out.println("ERROR not a number");
+											}
+
+											try {
+												wunlock();
+											} catch(Exception ex) {
+												break die;
+											}
+											try {
+												rlock();
+											} catch(Exception ex) {
+												break die;
+											}
+
+											try {
+												ip = nickinfo.exists(P_OUTPORT);
+											} catch(Exception ex) {
+												try {
+													runlock();
+												} catch(Exception ee) {
+													break die;
+												}
+												break die;
+											}
+											try {
+												runlock();
+											} catch(Exception ex) {
+												break die;
+											}
+
+											if(ip) {
+												out.println("OK outbound port set");
+											} else {
+												out.println("ERROR port out of range");
+											}
+
+										}
+									} catch(Exception ex) {
+										break die;
+									}
+
+								} else {
+									try {
+										nns(out);
+									} catch(Exception ex) {
+										break die;
+									}
+								}
+
+							} else if(Command.equals(C_inhost)) {
+								if(ns) {
+									try {
+										if(tunnelactive(nickinfo)) {
+											out.println("ERROR tunnel is active");
+										} else {
+											try {
+												wlock();
+											} catch(Exception ex) {
+												break die;
+											}
+											try {
+												nickinfo.add(P_INHOST, Arg);
+											} catch(Exception ex) {
+												try {
+													wunlock();
+												} catch(Exception ee) {
+													break die;
+												}
+												break die;
+											}
+											try {
+												wunlock();
+											} catch(Exception ex) {
+												break die;
+											}
+
+											out.println("OK inhost set");
+										}
+
+									} catch(Exception ex) {
+										break die;
+									}
+
+								} else {
+									try {
+										nns(out);
+									} catch(Exception ex) {
+										break die;
+									}
+								}
+
+							} else if(Command.equals(C_outhost)) {
+								if(ns) {
+									try {
+										if(tunnelactive(nickinfo)) {
+											out.println("ERROR tunnel is active");
+										} else {
+											try {
+												wlock();
+											} catch(Exception ex) {
+												break die;
+											}
+											try {
+												nickinfo.add(P_OUTHOST, Arg);
+											} catch(Exception ex) {
+												try {
+													wunlock();
+												} catch(Exception ee) {
+													break die;
+												}
+												break die;
+											}
+											try {
+												wunlock();
+											} catch(Exception ex) {
+												break die;
+											}
+
+											out.println("OK outhost set");
+										}
+
+									} catch(Exception ex) {
+										break die;
+									}
+
+								} else {
+									try {
+										nns(out);
+									} catch(Exception ex) {
+										break die;
+									}
+								}
+
+							} else if(Command.equals(C_show)) {
+								// Get the current NamedDB properties
+								if(ns) {
+									out.print("OK");
+									try {
+										rlock();
+									} catch(Exception e) {
+										break die;
+									}
+
+									try {
+										nickprint(out, nickinfo);
+									} 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_start)) {
+								// Start the tunnel, if we have all the information
+								if(ns && dk && (ip || op)) {
+									try {
+										if(tunnelactive(nickinfo)) {
+											out.println("ERROR tunnel is active");
+										} else {
+											MUXlisten tunnel;
+											try {
+												tunnel = new MUXlisten(database, nickinfo, _log);
+												Thread t = new Thread(tunnel);
+												t.start();
+												out.println("OK tunnel starting");
+											} catch(I2PException e) {
+												out.println("ERROR starting tunnel: " + e);
+											} catch(IOException e) {
+												out.println("ERROR starting tunnel: " + e);
+											}
+
+										}
+									} catch(Exception ex) {
+										break die;
+									}
+
+								} else {
+									out.println("ERROR tunnel settings incomplete");
+								}
+
+							} else if(Command.equals(C_stop)) {
+								// Stop the tunnel, if it is running
+								if(ns) {
+									try {
+										rlock();
+									} 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)) {
+											try {
+												runlock();
+											} catch(Exception e) {
+												break die;
+											}
+
+											try {
+												wlock();
+											} catch(Exception e) {
+												break die;
+											}
+
+											nickinfo.add(P_STOPPING, Boolean.TRUE);
+											try {
+												wunlock();
+
+											} catch(Exception e) {
+												break die;
+											}
+
+											out.println("OK tunnel stopping");
+										} else {
+											try {
+												runlock();
+											} catch(Exception e) {
+												break die;
+											}
+
+											out.println("ERROR tunnel is inactive");
+										}
+									} catch(Exception e) {
+										try {
+											runlock();
+										} catch(Exception ee) {
+											break die;
+										}
+										break die;
+									}
+
+								} else {
+									try {
+										nns(out);
+									} catch(Exception e) {
+										break die;
+									}
+								}
+
+							} else if(Command.equals(C_clear)) {
+								// Clear use of the NamedDB if stopped
+								if(ns) {
+									try {
+										if(tunnelactive(nickinfo)) {
+											out.println("ERROR tunnel is active");
+										} else {
+											try {
+												database.getWriteLock();
+											} catch(Exception e) {
+												break die;
+											}
+											try {
+												database.kill(nickinfo.get(P_NICKNAME));
+											} catch(Exception e) {
+												try {
+													database.releaseWriteLock();
+												} catch(Exception ee) {
+													break die;
+												}
+												break die;
+											}
+											try {
+												database.releaseWriteLock();
+											} catch(Exception e) {
+												break die;
+											}
+											dk = ns = ip = op = false;
+											out.println("OK cleared");
+										}
+
+									} catch(Exception ex) {
+										break die;
+									}
+
+								} else {
+									try {
+										nns(out);
+									} catch(Exception e) {
+										break die;
+									}
+								}
+
+							} else if(Command.equals(C_status)) {
+								try {
+									if(database.exists(Arg)) {
+										// Show status of a NamedDB
+										out.print("OK ");
+										try {
+											ttlpnt(out, Arg);
+										} catch(Exception e) {
+											out.println(); // this will cause an IOE if IOE
+											break die;
+										}
+
+									} else {
+										try {
+											nns(out);
+										} catch(Exception e) {
+											break die;
+										}
+									}
+								} catch(Exception e) {
+									break die;
+								}
+
+							} else {
+								out.println("ERROR UNKNOWN COMMAND! Try help");
+							}
+
+						}
+					}
+				} // die
+				out.print("ERROR A really bad error just happened, ");
+			} // quit
+// Say goodbye.
+
+			out.println("OK Bye!");
+
+			server.close();
+		} 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 c306c0ebf2de803e9ee2b8c355e829999ea53a35..c30e751f5c0456c91eadaecad9fa16ce675eb288 100644
--- a/apps/BOB/src/net/i2p/BOB/I2Plistener.java
+++ b/apps/BOB/src/net/i2p/BOB/I2Plistener.java
@@ -40,7 +40,7 @@ import net.i2p.util.Log;
  */
 public class I2Plistener implements Runnable {
 
-	private nickname info,  database;
+	private NamedDB info,  database;
 	private Log _log;
 	private int tgwatch;
 	public I2PSocketManager socketManager;
@@ -53,7 +53,7 @@ public class I2Plistener implements Runnable {
 	 * @param database
 	 * @param _log
 	 */
-	I2Plistener(I2PSocketManager S, nickname info, nickname database, Log _log) {
+	I2Plistener(I2PSocketManager S, NamedDB info, NamedDB database, Log _log) {
 		this.database = database;
 		this.info = info;
 		this._log = _log;
diff --git a/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java b/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java
index 4d5f239c6cff48723e27c7ab12ba6d05919d792d..dae9f3e0ff2392fd9b48c51a2dbc990924eaa114 100644
--- a/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java
+++ b/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java
@@ -36,7 +36,7 @@ import net.i2p.client.streaming.I2PSocket;
 public class I2PtoTCP implements Runnable {
 
 	private I2PSocket I2P;
-	private nickname info,  database;
+	private NamedDB info,  database;
 	private Socket sock;
 
 	/**
@@ -46,65 +46,93 @@ public class I2PtoTCP implements Runnable {
 	 * @param info
 	 * @param database
 	 */
-	I2PtoTCP(I2PSocket I2Psock, nickname info, nickname database) {
+	I2PtoTCP(I2PSocket I2Psock, NamedDB info, NamedDB database) {
 		this.I2P = I2Psock;
 		this.info = info;
 		this.database = database;
 	}
 
+	private void rlock() throws Exception {
+		database.getReadLock();
+		info.getReadLock();
+	}
+
+	private void runlock() throws Exception {
+		database.releaseReadLock();
+		info.releaseReadLock();
+	}
+
 	/**
 	 * I2P stream to TCP stream thread starter
 	 *
 	 */
 	public void run() {
-
-		try {
-			database.getReadLock();
-			info.getReadLock();
-			String host = info.get("OUTHOST").toString();
-			int port = Integer.parseInt(info.get("OUTPORT").toString());
-			boolean tell = info.get("QUIET").equals(Boolean.FALSE);
-			info.releaseReadLock();
-			database.releaseReadLock();
-			sock = new Socket(host, port);
-			// make readers/writers
-			InputStream in = sock.getInputStream();
-			OutputStream out = sock.getOutputStream();
-			InputStream Iin = I2P.getInputStream();
-			OutputStream Iout = I2P.getOutputStream();
-			I2P.setReadTimeout(0); // temp bugfix, this *SHOULD* be the default
-
-			if(tell) {
-				// tell who is connecting
-				out.write(I2P.getPeerDestination().toBase64().getBytes());
-				out.write(10); // nl
-				out.flush(); // not really needed, but...
-			}
-			// setup to cross the streams
-			TCPio conn_c = new TCPio(in, Iout, info, database); // app -> I2P
-			TCPio conn_a = new TCPio(Iin, out, info, database); // I2P -> app
-			Thread t = new Thread(conn_c, "TCPioA");
-			Thread q = new Thread(conn_a, "TCPioB");
-			// Fire!
-			t.start();
-			q.start();
-			while(t.isAlive() && q.isAlive()) { // AND is used here to kill off the other thread
+		String host;
+		int port;
+		boolean tell;
+die:            {
+			try {
 				try {
-					Thread.sleep(10); //sleep for 10 ms
-				} catch(InterruptedException e) {
-					// nop
+					rlock();
+				} catch(Exception e) {
+					break die;
 				}
-			}
+				try {
+					host = info.get("OUTHOST").toString();
+					port = Integer.parseInt(info.get("OUTPORT").toString());
+					tell = info.get("QUIET").equals(Boolean.FALSE);
+				} catch(Exception e) {
+					runlock();
+					break die;
+				}
+				try {
+					runlock();
+				} catch(Exception e) {
+					break die;
+				}
+				sock = new Socket(host, port);
+				// make readers/writers
+				InputStream in = sock.getInputStream();
+				OutputStream out = sock.getOutputStream();
+				InputStream Iin = I2P.getInputStream();
+				OutputStream Iout = I2P.getOutputStream();
+				I2P.setReadTimeout(0); // temp bugfix, this *SHOULD* be the default
 
-		} catch(Exception e) {
-		}
+				if(tell) {
+					// tell who is connecting
+					out.write(I2P.getPeerDestination().toBase64().getBytes());
+					out.write(10); // nl
+					out.flush(); // not really needed, but...
+				}
+				// setup to cross the streams
+				TCPio conn_c = new TCPio(in, Iout, info, database); // app -> I2P
+				TCPio conn_a = new TCPio(Iin, out, info, database); // I2P -> app
+				Thread t = new Thread(conn_c, "TCPioA");
+				Thread q = new Thread(conn_a, "TCPioB");
+				// Fire!
+				t.start();
+				q.start();
+				while(t.isAlive() && q.isAlive()) { // AND is used here to kill off the other thread
+					try {
+						Thread.sleep(10); //sleep for 10 ms
+					} catch(InterruptedException e) {
+						// nop
+					}
+				}
+
+			} catch(Exception e) {
+				break die;
+			}
+		} // die
 		try {
 			I2P.close();
 		} catch(Exception e) {
+			tell = false;
 		}
 		try {
 			sock.close();
 		} catch(Exception e) {
+			tell = false;
 		}
 	}
 }
diff --git a/apps/BOB/src/net/i2p/BOB/MUXlisten.java b/apps/BOB/src/net/i2p/BOB/MUXlisten.java
index 08b75efbb1ff24699a73abb4976f15274f820039..a16c830b1c0f011dcf2b2cef58a3e1b6a346650c 100644
--- a/apps/BOB/src/net/i2p/BOB/MUXlisten.java
+++ b/apps/BOB/src/net/i2p/BOB/MUXlisten.java
@@ -41,7 +41,7 @@ import net.i2p.util.Log;
  */
 public class MUXlisten implements Runnable {
 
-	private nickname database,  info;
+	private NamedDB database,  info;
 	private Log _log;
 	private I2PSocketManager socketManager;
 	private ByteArrayInputStream prikey;
@@ -51,6 +51,7 @@ public class MUXlisten implements Runnable {
 	private int backlog = 50; // should this be more? less?
 	boolean go_out;
 	boolean come_in;
+
 	/**
 	 * Constructor Will fail if INPORT is occupied.
 	 *
@@ -60,7 +61,7 @@ public class MUXlisten implements Runnable {
 	 * @throws net.i2p.I2PException
 	 * @throws java.io.IOException
 	 */
-	MUXlisten(nickname database, nickname info, Log _log) throws I2PException, IOException {
+	MUXlisten(NamedDB database, NamedDB info, Log _log) throws I2PException, IOException, RuntimeException {
 		int port = 0;
 		InetAddress host = null;
 		this.database = database;
@@ -90,7 +91,7 @@ public class MUXlisten implements Runnable {
 		if(this.come_in) {
 			this.listener = new ServerSocket(port, backlog, host);
 		}
-		
+
 		// Everything is OK as far as we can tell.
 		this.database.getWriteLock();
 		this.info.getWriteLock();
@@ -99,82 +100,143 @@ public class MUXlisten implements Runnable {
 		this.database.releaseWriteLock();
 	}
 
+	private void rlock() throws Exception {
+		database.getReadLock();
+		info.getReadLock();
+	}
+
+	private void runlock() throws Exception {
+		database.releaseReadLock();
+		info.releaseReadLock();
+	}
+
+	private void wlock() throws Exception {
+		database.getWriteLock();
+		info.getWriteLock();
+	}
+
+	private void wunlock() throws Exception {
+		info.releaseWriteLock();
+		database.releaseWriteLock();
+	}
+
 	/**
 	 * MUX sockets, fire off a thread to connect, get destination info, and do I/O
 	 *
 	 */
 	public void run() {
 
-		this.database.getWriteLock();
-		this.info.getWriteLock();
-		info.add("RUNNING", Boolean.TRUE);
-		info.add("STARTING", Boolean.FALSE);
-		this.info.releaseWriteLock();
-		this.database.releaseWriteLock();
-
 		try {
-			tg = new ThreadGroup(N);
-
-			// toss the connections to a new threads.
-			// will wrap with TCP and UDP when UDP works
-
-			if(go_out) {
-				// I2P -> TCP
-				I2Plistener conn = new I2Plistener(socketManager, info, database, _log);
-				Thread t = new Thread(tg, conn, "BOBI2Plistener " + N);
-				t.start();
-			}
-
-			if(come_in) {
-				// TCP -> I2P
-				TCPlistener conn = new TCPlistener(listener, socketManager, info, database, _log);
-				Thread q = new Thread(tg, conn, "BOBTCPlistener" + N);
-				q.start();
+			wlock();
+			try {
+				info.add("RUNNING", Boolean.TRUE);
+				info.add("STARTING", Boolean.FALSE);
+			} catch(Exception e) {
+				wunlock();
+				return;
 			}
+		} catch(Exception e) {
+			return;
+		}
+		try {
+			wunlock();
+		} catch(Exception e) {
+			return;
+		}
 
-			boolean spin = true;
-			while(spin) {
-				try {
-					Thread.sleep(1000); //sleep for 1000 ms (One second)
-				} catch(InterruptedException e) {
-					// nop
+quit:           {
+			try {
+				tg = new ThreadGroup(N);
+die:                            {
+					// toss the connections to a new threads.
+					// will wrap with TCP and UDP when UDP works
+
+					if(go_out) {
+						// I2P -> TCP
+						I2Plistener conn = new I2Plistener(socketManager, info, database, _log);
+						Thread t = new Thread(tg, conn, "BOBI2Plistener " + N);
+						t.start();
+					}
+
+					if(come_in) {
+						// TCP -> I2P
+						TCPlistener conn = new TCPlistener(listener, socketManager, info, database, _log);
+						Thread q = new Thread(tg, conn, "BOBTCPlistener" + N);
+						q.start();
+					}
+
+					boolean spin = true;
+					while(spin) {
+						try {
+							Thread.sleep(1000); //sleep for 1000 ms (One second)
+						} catch(InterruptedException e) {
+							// nop
+						}
+						try {
+							rlock();
+							try {
+								spin = info.get("STOPPING").equals(Boolean.FALSE);
+							} catch(Exception e) {
+								runlock();
+								break die;
+							}
+						} catch(Exception e) {
+							break die;
+						}
+						try {
+							runlock();
+						} catch(Exception e) {
+							break die;
+						}
+					}
+
+					try {
+						wlock();
+						try {
+							info.add("RUNNING", Boolean.FALSE);
+						} catch(Exception e) {
+							wunlock();
+							break die;
+						}
+					} catch(Exception e) {
+						break die;
+					}
+					try {
+						wunlock();
+					} catch(Exception e) {
+						break die;
+					}
+				} // die
+				// wait for child threads and thread groups to die
+				while(tg.activeCount() + tg.activeGroupCount() != 0) {
+					try {
+						Thread.sleep(1000); //sleep for 1000 ms (One second)
+					} catch(InterruptedException ex) {
+						// nop
+						}
 				}
-
-				this.database.getReadLock();
-				this.info.getReadLock();
-				spin = info.get("STOPPING").equals(Boolean.FALSE);
-				this.database.releaseReadLock();
-				this.info.releaseReadLock();
+				tg.destroy();
+				// Zap reference to the ThreadGroup so the JVM can GC it.
+				tg = null;
+			} catch(Exception e) {
+				break quit;
 			}
-
-			this.database.getWriteLock();
-			this.info.getWriteLock();
-			info.add("RUNNING", Boolean.FALSE);
-			this.info.releaseWriteLock();
-			this.database.releaseWriteLock();
-
-			// wait for child threads and thread groups to die
-			while(tg.activeCount() + tg.activeGroupCount() != 0) {
-				try {
-					Thread.sleep(1000); //sleep for 1000 ms (One second)
-				} catch(InterruptedException ex) {
-					// nop
-				}
+		} // quit
+		socketManager.destroySocketManager();
+		// zero out everything, just incase.
+		try {
+			wlock();
+			try {
+				info.add("STARTING", Boolean.FALSE);
+				info.add("STOPPING", Boolean.FALSE);
+				info.add("RUNNING", Boolean.FALSE);
+			} catch(Exception e) {
+				wunlock();
+				return;
 			}
-			tg.destroy();
-			// Zap reference to the ThreadGroup so the JVM can GC it.
-			tg = null;
+			wunlock();
 		} catch(Exception e) {
+			return;
 		}
-
-		socketManager.destroySocketManager();
-		// zero out everything, just incase.
-		this.database.getWriteLock();
-		this.info.getWriteLock();
-		info.add("STARTING", Boolean.FALSE);
-		info.add("STOPPING", Boolean.FALSE);
-		info.add("RUNNING", Boolean.FALSE);
-		this.info.releaseWriteLock();
-		this.database.releaseWriteLock();
 	}
 }
diff --git a/apps/BOB/src/net/i2p/BOB/nickname.java b/apps/BOB/src/net/i2p/BOB/NamedDB.java
similarity index 95%
rename from apps/BOB/src/net/i2p/BOB/nickname.java
rename to apps/BOB/src/net/i2p/BOB/NamedDB.java
index 37f16bb18df22b371470aa480637f52dfef1ecbf..8b0ba35df6a1079c53609b73135c5fd8f58d30da 100644
--- a/apps/BOB/src/net/i2p/BOB/nickname.java
+++ b/apps/BOB/src/net/i2p/BOB/NamedDB.java
@@ -28,7 +28,7 @@ package net.i2p.BOB;
  *
  * @author sponge
  */
-public class nickname {
+public class NamedDB {
 
 	private volatile Object[][] data;
 	private volatile int index,  writersWaiting,  readers;
@@ -37,7 +37,7 @@ public class nickname {
 	 * make initial NULL object
 	 *
 	 */
-	public nickname() {
+	public NamedDB() {
 		this.data = new Object[1][2];
 		this.index = this.writersWaiting = this.readers = 0;
 	}
@@ -76,8 +76,9 @@ public class nickname {
 	 * Find objects in the array, returns it's index or throws exception
 	 * @param key
 	 * @return an objects index
+	 * @throws ArrayIndexOutOfBoundsException when key does not exist
 	 */
-	public int idx(Object key) {
+	public int idx(Object key) throws ArrayIndexOutOfBoundsException {
 		for(int i = 0; i < index; i++) {
 			if(key.equals(data[i][0])) {
 				return i;
@@ -115,7 +116,6 @@ public class nickname {
 		}
 		index -= didsomething;
 		data = olddata;
-
 	}
 
 	/**
diff --git a/apps/BOB/src/net/i2p/BOB/TCPio.java b/apps/BOB/src/net/i2p/BOB/TCPio.java
index f6286f3290ca5420c06a1cbb227d5176fe9bb87f..e9024105afa93b67440ed73f19855d470a22408a 100644
--- a/apps/BOB/src/net/i2p/BOB/TCPio.java
+++ b/apps/BOB/src/net/i2p/BOB/TCPio.java
@@ -36,7 +36,7 @@ public class TCPio implements Runnable {
 
 	private InputStream Ain;
 	private OutputStream Aout;
-	private nickname info,  database;
+	private NamedDB info,  database;
 
 	/**
 	 * Constructor
@@ -46,16 +46,13 @@ public class TCPio implements Runnable {
 	 * @param info
 	 * @param database
 	 */
-	TCPio(InputStream Ain, OutputStream Aout, nickname info, nickname database) {
+	TCPio(InputStream Ain, OutputStream Aout, NamedDB info, NamedDB database) {
 		this.Ain = Ain;
 		this.Aout = Aout;
 		this.info = info;
 		this.database = database;
 	}
 
-	/**
-	 * kill off the streams, to hopefully cause an IOException in the thread in order to kill it.
-	 */
 	/**
 	 * Copy from source to destination...
 	 * and yes, we are totally OK to block here on writes,
@@ -73,17 +70,18 @@ public class TCPio implements Runnable {
 				spin = info.get("RUNNING").equals(Boolean.TRUE);
 				info.releaseReadLock();
 				database.releaseReadLock();
-
 				b = Ain.read(a, 0, 1);
 				// System.out.println(info.get("NICKNAME").toString() + " " + b);
 				if(b > 0) {
-					Aout.write(a, 0, 1);
-				// Aout.flush(); too slow!
+					Aout.write(a, 0, b);
 				} else if(b == 0) {
-					try {
-						// Thread.yield();
-						Thread.sleep(10);
-					} catch(InterruptedException ex) {
+					Thread.yield(); // this should act like a mini sleep.
+					if(Ain.available() == 0) {
+						try {
+							// Thread.yield();
+							Thread.sleep(10);
+						} catch(InterruptedException ex) {
+						}
 					}
 				} else {
 					/* according to the specs:
@@ -97,6 +95,8 @@ public class TCPio implements Runnable {
 				}
 			}
 		} catch(Exception e) {
+			// Eject!!! Eject!!!
+			return;
 		}
 	}
 }
diff --git a/apps/BOB/src/net/i2p/BOB/TCPlistener.java b/apps/BOB/src/net/i2p/BOB/TCPlistener.java
index 9134c0a47fa5c147a27d5a3db445066b144306a1..72b34eac86d014688b1edac109805f8f691ec946 100644
--- a/apps/BOB/src/net/i2p/BOB/TCPlistener.java
+++ b/apps/BOB/src/net/i2p/BOB/TCPlistener.java
@@ -40,7 +40,7 @@ import net.i2p.util.Log;
  */
 public class TCPlistener implements Runnable {
 
-	private nickname info,  database;
+	private NamedDB info,  database;
 	private Log _log;
 	private int tgwatch;
 	public I2PSocketManager socketManager;
@@ -54,7 +54,7 @@ public class TCPlistener implements Runnable {
 	 * @param database
 	 * @param _log
 	 */
-	TCPlistener(ServerSocket listener, I2PSocketManager S, nickname info, nickname database, Log _log) {
+	TCPlistener(ServerSocket listener, I2PSocketManager S, NamedDB info, NamedDB database, Log _log) {
 		this.database = database;
 		this.info = info;
 		this._log = _log;
diff --git a/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java b/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java
index dd199ecb7cbe925e68c4f562aa20bb9183a47b2c..3323d0450b1cd5491f74d71a4cce5fd812a86af3 100644
--- a/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java
+++ b/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java
@@ -45,7 +45,7 @@ import net.i2p.i2ptunnel.I2PTunnel;
 public class TCPtoI2P implements Runnable {
 
 	private I2PSocket I2P;
-	private nickname info,  database;
+	private NamedDB info,  database;
 	private Socket sock;
 	private I2PSocketManager socketManager;
 
@@ -57,7 +57,7 @@ public class TCPtoI2P implements Runnable {
 	 * @return line of text as a String
 	 * @throws Exception
 	 */
-	public static String Lread(InputStream in) throws Exception {
+	private static String lnRead(InputStream in) throws Exception {
 		String S;
 		int b;
 		char c;
@@ -87,7 +87,7 @@ public class TCPtoI2P implements Runnable {
 	 * @param info
 	 * @param database
 	 */
-	TCPtoI2P(I2PSocketManager i2p, Socket socket, nickname info, nickname database) {
+	TCPtoI2P(I2PSocketManager i2p, Socket socket, NamedDB info, NamedDB database) {
 		this.sock = socket;
 		this.info = info;
 		this.database = database;
@@ -119,7 +119,7 @@ public class TCPtoI2P implements Runnable {
 			InputStream in = sock.getInputStream();
 			OutputStream out = sock.getOutputStream();
 			try {
-				line = Lread(in);
+				line = lnRead(in);
 				input = line.toLowerCase();
 				Destination dest = null;
 
diff --git a/apps/BOB/src/net/i2p/BOB/UDPIOthread.java b/apps/BOB/src/net/i2p/BOB/UDPIOthread.java
index 0dce4a0c271c8740d96214d52259d414eadae69e..687fe80893cf6e94e599953f8816b754115ed9a7 100644
--- a/apps/BOB/src/net/i2p/BOB/UDPIOthread.java
+++ b/apps/BOB/src/net/i2p/BOB/UDPIOthread.java
@@ -45,7 +45,7 @@ import net.i2p.util.Log;
  */
 public class UDPIOthread implements I2PSessionListener, Runnable {
 
-	private nickname info;
+	private NamedDB info;
 	private Log _log;
 	private Socket socket;
 	private DataInputStream in;
@@ -61,7 +61,7 @@ public class UDPIOthread implements I2PSessionListener, Runnable {
 	 * @param socket
 	 * @param _session
 	 */
-	UDPIOthread(nickname info, Log _log, Socket socket, I2PSession _session) {
+	UDPIOthread(NamedDB info, Log _log, Socket socket, I2PSession _session) {
 		this.info = info;
 		this._log = _log;
 		this.socket = socket;
diff --git a/apps/BOB/src/net/i2p/BOB/doCMDS.java b/apps/BOB/src/net/i2p/BOB/doCMDS.java
deleted file mode 100644
index e87a91b62c98fd7bba4e16740adc158e314b4b14..0000000000000000000000000000000000000000
--- a/apps/BOB/src/net/i2p/BOB/doCMDS.java
+++ /dev/null
@@ -1,729 +0,0 @@
-/**
- *            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.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintStream;
-import java.net.Socket;
-import java.util.Properties;
-import java.util.StringTokenizer;
-import net.i2p.I2PException;
-import net.i2p.client.I2PClientFactory;
-import net.i2p.data.Destination;
-import net.i2p.util.Log;
-
-/**
- * Simplistic command parser for BOB
- *
- * @author sponge
- *
- */
-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 = "-B";
-	public static final String BOBversion = BMAJ + "." + BMIN + "." + BREV + BEXT;
-	private Socket server;
-	private Properties props;
-	private nickname database;
-	private String line;
-	private Destination d;
-	private ByteArrayOutputStream prikey;
-	private boolean dk,  ns,  ip,  op;
-	private nickname nickinfo;
-	private Log _log;
-	/* database strings */
-	private static final String P_DEST = "DESTINATION";
-	private static final String P_INHOST = "INHOST";
-	private static final String P_INPORT = "INPORT";
-	private static final String P_KEYS = "KEYS";
-	private static final String P_NICKNAME = "NICKNAME";
-	private static final String P_OUTHOST = "OUTHOST";
-	private static final String P_OUTPORT = "OUTPORT";
-	private static final String P_PROPERTIES = "PROPERTIES";
-	private static final String P_QUIET = "QUIET";
-	private static final String P_RUNNING = "RUNNING";
-	private static final String P_STARTING = "STARTING";
-	private static final String P_STOPPING = "STOPPING";
-//	private static final String P_INSTATE = "INSTATE";
-//	private static final String P_OUTSTATE = "OUTSTATE";
-
-	/* command strings */
-	private static final String C_help = "help";
-	private static final String C_clear = "clear";
-	private static final String C_getdest = "getdest";
-	private static final String C_getkeys = "getkeys";
-	private static final String C_getnick = "getnick";
-	private static final String C_inhost = "inhost";
-	private static final String C_inport = "inport";
-	private static final String C_list = "list";
-	private static final String C_newkeys = "newkeys";
-	private static final String C_option = "option";
-	private static final String C_outhost = "outhost";
-	private static final String C_outport = "outport";
-	private static final String C_quiet = "quiet";
-	private static final String C_quit = "quit";
-	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_start = "start";
-	private static final String C_status = "status";
-	private static final String C_stop = "stop";
-
-	/* all the coomands available, plus description */
-	private static final String C_ALL[][] = {
-		{C_help, C_help + " <command> * Get help on a command."},
-		{C_clear, C_clear + " * Clear the current nickname out of the list."},
-		{C_getdest, C_getdest + " * Return the destination for the current nickname."},
-		{C_getkeys, C_getkeys + " * Return the keypair for the current nickname."},
-		{C_getnick, C_getnick + " tunnelname * Set the nickname from the database."},
-		{C_inhost, C_inhost + " hostname | IP * Set the inbound hostname or IP."},
-		{C_inport, C_inport + " port_number * Set the inbound port number nickname listens on."},
-		{C_list, C_list + " * List all tunnels."},
-		{C_newkeys, C_newkeys + " * Generate a new keypair for the current nickname."},
-		{C_option, C_option + " I2CPoption=something * Set an I2CP option. NOTE: Don't use any spaces."},
-		{C_outhost, C_outhost + " hostname | IP * Set the outbound hostname or IP."},
-		{C_outport, C_outport + " port_number * Set the outbound port that nickname contacts."},
-		{C_quiet, C_quiet + " True | False * Don't send to the application the incoming destination."},
-		{C_quit, C_quit + " * Quits this session with BOB."},
-		{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_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."},
-		{"", "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
-		},
-		{" ", " "} // end of list
-	};
-
-	/**
-	 *
-	 * @param server
-	 * @param props
-	 * @param database
-	 * @param _log
-	 */
-	doCMDS(Socket server, Properties props, nickname database, Log _log) {
-		this.server = server;
-		this.props = new Properties(props);
-		this.database = database;
-		this._log = _log;
-	}
-
-	private void rlock() {
-		rlock(nickinfo);
-	}
-
-	private void rlock(nickname Arg) {
-		database.getReadLock();
-		Arg.getReadLock();
-	}
-
-	private void runlock() {
-		runlock(nickinfo);
-	}
-
-	private void runlock(nickname Arg) {
-		Arg.releaseReadLock();
-		database.releaseReadLock();
-	}
-
-	private void wlock() {
-		wlock(nickinfo);
-	}
-
-	private void wlock(nickname Arg) {
-		database.getWriteLock();
-		Arg.getWriteLock();
-	}
-
-	private void wunlock() {
-		wunlock(nickinfo);
-	}
-
-	private void wunlock(nickname Arg) {
-		Arg.releaseWriteLock();
-		database.releaseWriteLock();
-	}
-
-	/**
-	 * Try to print info from the database
-	 *
-	 * @param out
-	 * @param info
-	 * @param key
-	 */
-	public void trypnt(PrintStream out, nickname info, Object key) {
-		rlock(info);
-		try {
-			out.print(" " + key + ": ");
-			if(info.exists(key)) {
-				out.print(info.get(key));
-			} else {
-				out.print("not_set");
-			}
-		} catch(Exception e) {
-		}
-		runlock(info);
-	}
-
-	/**
-	 * Print true or false if an object exists
-	 *
-	 * @param out
-	 * @param info
-	 * @param key
-	 */
-	public void tfpnt(PrintStream out, nickname info, Object key) {
-		rlock(info);
-		try {
-			out.print(" " + key + ": ");
-			out.print(info.exists(key));
-		} catch(Exception e) {
-		}
-		runlock(info);
-	}
-
-	/**
-	 * Print an error message
-	 *
-	 * @param out
-	 */
-	public void nns(PrintStream out) {
-		out.println("ERROR no nickname has been set");
-	}
-
-	/**
-	 * Dump various information from the database
-	 *
-	 * @param out
-	 * @param info
-	 */
-	public void nickprint(PrintStream out, nickname info) {
-		rlock(info);
-		trypnt(out, info, P_NICKNAME);
-		trypnt(out, info, P_STARTING);
-		trypnt(out, info, P_RUNNING);
-		trypnt(out, info, P_STOPPING);
-		tfpnt(out, info, P_KEYS);
-		trypnt(out, info, P_QUIET);
-		trypnt(out, info, P_INPORT);
-		trypnt(out, info, P_INHOST);
-		trypnt(out, info, P_OUTPORT);
-		trypnt(out, info, P_OUTHOST);
-		try {
-			out.println();
-		} catch(Exception e) {
-		}
-		runlock(info);
-	}
-
-	/**
-	 * Print information on a specific record, indicated by nickname
-	 * @param out
-	 * @param Arg
-	 */
-	public void ttlpnt(PrintStream out, Object Arg) {
-		database.getReadLock();
-		if(database.exists(Arg)) {
-			try {
-				out.print("DATA");
-			} catch(Exception e) {
-			}
-			nickprint(out, (nickname)database.get(Arg));
-		}
-		database.releaseReadLock();
-	}
-
-	/**
-	 * Is this nickname's tunnel active?
-	 *
-	 * @param Arg
-	 * @return true if the tunnel is active
-	 */
-	public boolean tunnelactive(nickname Arg) {
-		rlock(Arg);
-		boolean retval = (Arg.get(P_STARTING).equals(Boolean.TRUE) ||
-			Arg.get(P_STOPPING).equals(Boolean.TRUE) ||
-			Arg.get(P_RUNNING).equals(Boolean.TRUE));
-		runlock(Arg);
-		return retval;
-	}
-
-	/**
-	 * Does the base64 information look OK
-	 *
-	 * @param data
-	 * @return
-	 */
-	private boolean is64ok(String data) {
-		String dest = new String(data);
-		if(dest.replaceAll("[a-zA-Z0-9~-]", "").length() == 0) {
-			return true;
-		}
-		return false;
-	}
-
-	/**
-	 * The actual parser.
-	 * It probabbly needs a rewrite into functions, but I kind-of like inline code.
-	 *
-	 */
-	public void run() {
-		dk = ns = ip = op = false;
-
-		try {
-			// Get input from the client
-			BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));
-			PrintStream out = new PrintStream(server.getOutputStream());
-			prikey = new ByteArrayOutputStream();
-			out.println("BOB " + BOBversion);
-			out.println("OK");
-			while((line = in.readLine()) != null) {
-				StringTokenizer token = new StringTokenizer(line, " "); // use a space as a delimiter
-				String Command = "";
-				String Arg = "";
-				nickname info;
-
-				if(token.countTokens() != 0) {
-					Command = token.nextToken();
-					Command = Command.toLowerCase();
-					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)) {
-								out.println("OK " + C_ALL[i][1]);
-							}
-						}
-					} else if(Command.equals(C_getdest)) {
-						if(ns) {
-							if(dk) {
-								rlock();
-								try {
-									out.println("OK " + nickinfo.get(P_DEST));
-								} catch(Exception e) {
-								}
-								runlock();
-							} else {
-								out.println("ERROR keys not set.");
-							}
-						} else {
-							nns(out);
-						}
-					} else if(Command.equals(C_list)) {
-						// Produce a formatted list of all nicknames
-						database.getReadLock();
-						for(int i = 0; i < database.getcount(); i++) {
-							try {
-								info = (nickname)database.getnext(i);
-							} catch(Exception b) {
-								break; // something bad happened.
-							}
-							try {
-
-								out.print("DATA");
-							} catch(Exception e) {
-							}
-							info.getReadLock();
-							nickprint(out, info);
-							info.releaseReadLock();
-						}
-						database.releaseReadLock();
-						out.println("OK Listing done");
-					} else if(Command.equals(C_quit)) {
-						// End the command session
-						break;
-					} else if(Command.equals(C_newkeys)) {
-						if(ns) {
-							if(tunnelactive(nickinfo)) {
-								out.println("ERROR tunnel is active");
-							} else {
-								try {
-									// Make a new PublicKey and PrivateKey
-									prikey = new ByteArrayOutputStream();
-									d = I2PClientFactory.createClient().createDestination(prikey);
-									wlock();
-									nickinfo.add(P_KEYS, prikey.toByteArray());
-									nickinfo.add(P_DEST, d.toBase64());
-									dk = true;
-									wunlock();
-									rlock();
-									try {
-										out.println("OK " + nickinfo.get(P_DEST));
-									} catch(Exception e) {
-									}
-									runlock();
-								} catch(IOException ioe) {
-									BOB.error("Error generating keys" + ioe);
-									out.println("ERROR generating keys");
-								} catch(I2PException ipe) {
-									BOB.error("Error generating keys" + ipe);
-									out.println("ERROR generating keys");
-								}
-							}
-						} else {
-							nns(out);
-						}
-					} else if(Command.equals(C_getkeys)) {
-						// Return public key
-						if(dk) {
-							prikey = new ByteArrayOutputStream();
-							rlock();
-							prikey.write(((byte[])nickinfo.get(P_KEYS)));
-							runlock();
-							out.println("OK " + net.i2p.data.Base64.encode(prikey.toByteArray()));
-						} else {
-							out.println("ERROR no public key has been set");
-						}
-					} else if(Command.equals(C_quiet)) {
-						if(ns) {
-							if(tunnelactive(nickinfo)) {
-								out.println("ERROR tunnel is active");
-							} else {
-								wlock();
-								nickinfo.add(P_QUIET, new Boolean(Boolean.parseBoolean(Arg) == true));
-								wunlock();
-								out.println("OK Quiet set");
-							}
-						} else {
-							nns(out);
-						}
-					} else if(Command.equals(C_setkeys)) {
-						// Set the nickname to a privatekey in BASE64 format
-						if(ns) {
-							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) {
-									Arg = "";
-								}
-								if((Arg.length() == 884) && is64ok(Arg)) {
-									wlock();
-									nickinfo.add(P_KEYS, prikey.toByteArray());
-									nickinfo.add(P_DEST, d.toBase64());
-									dk = true;
-									wunlock();
-									rlock();
-									try {
-										out.println("OK " + nickinfo.get(P_DEST));
-									} catch(Exception e) {
-									}
-									runlock();
-								} else {
-									out.println("ERROR not in BASE64 format");
-								}
-							}
-						} else {
-							nns(out);
-						}
-					} else if(Command.equals(C_setnick)) {
-						ns = dk = ip = op = false;
-						database.getReadLock();
-						try {
-							nickinfo = (nickname)database.get(Arg);
-							if(!tunnelactive(nickinfo)) {
-								nickinfo = null;
-								ns = true;
-							}
-						} catch(Exception b) {
-							nickinfo = null;
-							ns = true;
-						}
-						database.releaseReadLock();
-						// Clears and Sets the initial nickname structure to work with
-						if(ns) {
-							nickinfo = new nickname();
-							wlock();
-							database.add(Arg, nickinfo);
-							nickinfo.add(P_NICKNAME, Arg);
-							nickinfo.add(P_STARTING, Boolean.FALSE);
-							nickinfo.add(P_RUNNING, Boolean.FALSE);
-							nickinfo.add(P_STOPPING, Boolean.FALSE);
-							nickinfo.add(P_QUIET, Boolean.FALSE);
-							nickinfo.add(P_INHOST, "localhost");
-							nickinfo.add(P_OUTHOST, "localhost");
-							Properties Q = new Properties(props);
-							Q.setProperty("inbound.nickname", Arg);
-							Q.setProperty("outbound.nickname", Arg);
-							nickinfo.add(P_PROPERTIES, Q);
-							wunlock();
-							out.println("OK Nickname set to " + Arg);
-						} else {
-							out.println("ERROR tunnel is active");
-						}
-					} else if(Command.equals(C_option)) {
-						if(ns) {
-							if(tunnelactive(nickinfo)) {
-								out.println("ERROR tunnel is active");
-							} else {
-								StringTokenizer otoken = new StringTokenizer(Arg, "="); // use a space as a delimiter
-								if(otoken.countTokens() != 2) {
-									out.println("ERROR to many or no options.");
-								} else {
-									String pname = otoken.nextToken();
-									String pval = otoken.nextToken();
-									rlock();
-									Properties Q = (Properties)nickinfo.get(P_PROPERTIES);
-									runlock();
-									Q.setProperty(pname, pval);
-									wlock();
-									nickinfo.add(P_PROPERTIES, Q);
-									wunlock();
-									out.println("OK " + pname + " set to " + pval);
-								}
-							}
-						} else {
-							nns(out);
-						}
-					} else if(Command.equals(C_getnick)) {
-						// Get the nickname to work with...
-						database.getReadLock();
-						try {
-							nickinfo = (nickname)database.get(Arg);
-							ns = true;
-						} catch(RuntimeException b) {
-							nns(out);
-						}
-						database.releaseReadLock();
-						if(ns) {
-							rlock();
-							dk = nickinfo.exists(P_KEYS);
-							ip = nickinfo.exists(P_INPORT);
-							op = nickinfo.exists(P_OUTPORT);
-							runlock();
-							// Finally say OK.
-							out.println("OK Nickname set to " + Arg);
-						}
-					} else if(Command.equals(C_inport)) {
-						// Set the nickname inbound TO the router port
-						// app --> BOB
-						if(ns) {
-							if(tunnelactive(nickinfo)) {
-								out.println("ERROR tunnel is active");
-							} else {
-								int prt;
-								wlock();
-								nickinfo.kill(P_INPORT);
-								try {
-									prt = Integer.parseInt(Arg);
-									if(prt > 1 && prt < 65536) {
-										nickinfo.add(P_INPORT, new Integer(prt));
-									}
-								} catch(NumberFormatException nfe) {
-									out.println("ERROR not a number");
-								}
-								wunlock();
-								rlock();
-								ip = nickinfo.exists(P_INPORT);
-								runlock();
-								if(ip) {
-									out.println("OK inbound port set");
-								} else {
-									out.println("ERROR port out of range");
-								}
-							}
-						} else {
-							nns(out);
-						}
-					} else if(Command.equals(C_outport)) {
-						// Set the nickname outbound FROM the router port
-						// BOB --> app
-						if(ns) {
-							if(tunnelactive(nickinfo)) {
-								out.println("ERROR tunnel is active");
-							} else {
-								int prt;
-								wlock();
-								nickinfo.kill(P_OUTPORT);
-								try {
-									prt = Integer.parseInt(Arg);
-									if(prt > 1 && prt < 65536) {
-										nickinfo.add(P_OUTPORT, new Integer(prt));
-									}
-								} catch(NumberFormatException nfe) {
-									out.println("ERROR not a number");
-								}
-								wunlock();
-								rlock();
-								ip = nickinfo.exists(P_OUTPORT);
-								runlock();
-								if(ip) {
-									out.println("OK outbound port set");
-								} else {
-									out.println("ERROR port out of range");
-								}
-							}
-						} else {
-							nns(out);
-						}
-					} else if(Command.equals(C_inhost)) {
-						if(ns) {
-							if(tunnelactive(nickinfo)) {
-								out.println("ERROR tunnel is active");
-							} else {
-								wlock();
-								nickinfo.add(P_INHOST, Arg);
-								wunlock();
-								out.println("OK inhost set");
-							}
-						} else {
-							nns(out);
-						}
-					} else if(Command.equals(C_outhost)) {
-						if(ns) {
-							if(tunnelactive(nickinfo)) {
-								out.println("ERROR tunnel is active");
-							} else {
-								wlock();
-								nickinfo.add(P_OUTHOST, Arg);
-								wunlock();
-								out.println("OK outhost set");
-							}
-						} else {
-							nns(out);
-						}
-					} else if(Command.equals(C_show)) {
-						// Get the current nickname properties
-						if(ns) {
-							out.print("OK");
-							rlock();
-							nickprint(out, nickinfo);
-							runlock();
-						} else {
-							nns(out);
-						}
-					} else if(Command.equals(C_start)) {
-						// Start the tunnel, if we have all the information
-						if(ns && dk && (ip || op)) {
-							if(tunnelactive(nickinfo)) {
-								out.println("ERROR tunnel is active");
-							} else {
-								MUXlisten tunnel;
-								try {
-									tunnel = new MUXlisten(database, nickinfo, _log);
-									Thread t = new Thread(tunnel);
-									t.start();
-									out.println("OK tunnel starting");
-								} catch(I2PException e) {
-									out.println("ERROR starting tunnel: " + e);
-								} catch(IOException e) {
-									out.println("ERROR starting tunnel: " + e);
-								}
-							}
-						} else {
-							out.println("ERROR tunnel settings incomplete");
-						}
-					} else if(Command.equals(C_stop)) {
-						// Stop the tunnel, if it is running
-						if(ns) {
-							rlock();
-							if(nickinfo.get(P_RUNNING).equals(Boolean.TRUE) && nickinfo.get(P_STOPPING).equals(Boolean.FALSE) && nickinfo.get(P_STARTING).equals(Boolean.FALSE)) {
-								runlock();
-								wlock();
-								nickinfo.add(P_STOPPING, Boolean.TRUE);
-								wunlock();
-								out.println("OK tunnel stopping");
-							} else {
-								runlock();
-								out.println("ERROR tunnel is inactive");
-							}
-						} else {
-							nns(out);
-						}
-					} else if(Command.equals(C_clear)) {
-						// Clear use of the nickname if stopped
-						if(ns) {
-							if(tunnelactive(nickinfo)) {
-								out.println("ERROR tunnel is active");
-							} else {
-								database.getWriteLock();
-								database.kill(nickinfo.get(P_NICKNAME));
-								database.releaseWriteLock();
-								dk = ns = ip = op = false;
-								out.println("OK cleared");
-							}
-						} else {
-							nns(out);
-						}
-					} else if(Command.equals(C_status)) {
-						if(database.exists(Arg)) {
-							// Show status of a nickname
-							out.print("OK ");
-							ttlpnt(out, Arg);
-						} else {
-							nns(out);
-						}
-					} else {
-						out.println("ERROR UNKNOWN COMMAND! Try help");
-					}
-				}
-			}
-
-			// Say goodbye.
-
-			out.println("OK Bye!");
-
-			server.close();
-		} catch(IOException ioe) {
-			BOB.warn("IOException on socket listen: " + ioe);
-			ioe.printStackTrace();
-		}
-	}
-}