diff --git a/apps/BOB/src/net/i2p/BOB/DoCMDS.java b/apps/BOB/src/net/i2p/BOB/DoCMDS.java
index 6033e303bf2593c000884527ed5f0fdbd4311cf7..099d69fecac4e6d6a6378aac9be51a6216891b14 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 = "03",  BEXT = "";
+	public static final String BMAJ = "00",  BMIN = "00",  BREV = "04",  BEXT = "";
 	public static final String BOBversion = BMAJ + "." + BMIN + "." + BREV + BEXT;
 	private Socket server;
 	private Properties props;
diff --git a/apps/BOB/src/net/i2p/BOB/I2Plistener.java b/apps/BOB/src/net/i2p/BOB/I2Plistener.java
index 1561b7a222891c3d147453e3d97dc4a3ec9547a8..c59683270e585b5127ba8020ee4768a49fec6a02 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(100);
+		serverSocket.setSoTimeout(50);
 		database.getReadLock();
 		info.getReadLock();
 		if(info.exists("INPORT")) {
diff --git a/apps/BOB/src/net/i2p/BOB/MUXlisten.java b/apps/BOB/src/net/i2p/BOB/MUXlisten.java
index bd52e27fd2deac44d2a0f06b1e20aaa5be4f832f..89ab53fe62f9f8fad5afee5c08d1a511b2a735c9 100644
--- a/apps/BOB/src/net/i2p/BOB/MUXlisten.java
+++ b/apps/BOB/src/net/i2p/BOB/MUXlisten.java
@@ -173,7 +173,7 @@ die:                            {
 					boolean spin = true;
 					while(spin) {
 						try {
-							Thread.sleep(1000); //sleep for 1000 ms (One second)
+							Thread.sleep(200); //sleep for 200 ms (Two thenths second)
 						} catch(InterruptedException e) {
 							// nop
 						}
@@ -213,14 +213,21 @@ die:                            {
 					}
 				} // die
 
+				try {
+					Thread.sleep(500); //sleep for 500 ms (One half second)
+				} catch(InterruptedException ex) {
+					// nop
+				}
 				// wait for child threads and thread groups to die
 				// System.out.println("MUXlisten: waiting for children");
-				while(tg.activeCount() + tg.activeGroupCount() != 0) {
+				if(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
+					while(tg.activeCount() + tg.activeGroupCount() != 0) {
+						try {
+							Thread.sleep(100); //sleep for 100 ms (One tenth second)
+						} catch(InterruptedException ex) {
+							// nop
+						}
 					}
 				}
 				tg.destroy();
@@ -260,17 +267,33 @@ die:                            {
 		}
 		// This is here to catch when something fucks up REALLY bad.
 		if(tg != null) {
-			while(tg.activeCount() + tg.activeGroupCount() != 0) {
+			if(tg.activeCount() + tg.activeGroupCount() != 0) {
 					tg.interrupt(); // unwedge any blocking threads.
+				while(tg.activeCount() + tg.activeGroupCount() != 0) {
 					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;
 		}
+
+		// Lastly try to close things again.
+		if(this.come_in) {
+			try {
+				listener.close();
+			} catch(IOException e) {
+			}
+		}
+		try {
+			socketManager.destroySocketManager();
+		} catch(Exception e) {
+			// nop
+		}
+
 	}
 }
diff --git a/apps/BOB/src/net/i2p/BOB/TCPio.java b/apps/BOB/src/net/i2p/BOB/TCPio.java
index 25290bcdcf1d15630848a5c414a11989482f229d..41bb7cbe494d658cd828728c87a2debc4672b6f0 100644
--- a/apps/BOB/src/net/i2p/BOB/TCPio.java
+++ b/apps/BOB/src/net/i2p/BOB/TCPio.java
@@ -56,9 +56,28 @@ public class TCPio implements Runnable {
 	 * Copy from source to destination...
 	 * and yes, we are totally OK to block here on writes,
 	 * The OS has buffers, and I intend to use them.
+	 * We send an interrupt signal to the threadgroup to
+	 * unwedge any pending writes.
 	 *
 	 */
 	public void run() {
+		/*
+		 * NOTE:
+		 * The write method of OutputStream calls the write method of
+		 * one argument on each of the bytes to be written out.
+		 * Subclasses are encouraged to override this method and provide
+		 * a more efficient implementation.
+		 *
+		 * So, is this really a performance problem?
+		 * Should we expand to several bytes?
+		 * I don't believe there would be any gain, since read method
+		 * has the same reccomendations. If anyone has a better way to
+		 * do this, I'm interested in performance improvements.
+		 *
+		 * --Sponge
+		 *
+		 */
+
 		int b;
 		byte a[] = new byte[1];
 		boolean spin = true;
diff --git a/apps/BOB/src/net/i2p/BOB/TCPlistener.java b/apps/BOB/src/net/i2p/BOB/TCPlistener.java
index 99ae047d31cb10f7d698d28a59e0ae09ed65fe06..30380a55dd0241fb104fb36a71dfbfaa4c6959de 100644
--- a/apps/BOB/src/net/i2p/BOB/TCPlistener.java
+++ b/apps/BOB/src/net/i2p/BOB/TCPlistener.java
@@ -77,7 +77,7 @@ public class TCPlistener implements Runnable {
 		}
 		try {
 			Socket server = new Socket();
-			listener.setSoTimeout(1000);
+			listener.setSoTimeout(50); // Half of the expected time from MUXlisten
 			info.releaseReadLock();
 			database.releaseReadLock();
 			while(spin) {
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java
index aa95e526c809a166f44bd219064a271d413adc7d..7e12aa30a6162737cd26f32e499b6e9e22d68287 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java
@@ -16,6 +16,7 @@ import net.i2p.data.DataFormatException;
 import net.i2p.data.DataHelper;
 import net.i2p.data.Destination;
 import net.i2p.data.Hash;
+import net.i2p.data.Base32;
 import net.i2p.util.EventDispatcher;
 import net.i2p.util.I2PThread;
 import net.i2p.util.Log;
@@ -36,26 +37,27 @@ import net.i2p.util.Log;
  * "custom options" section of i2ptunnel.
  *   - ircserver.cloakKey unset:          Cloak with a random value that is persistent for
  *                                        the life of this tunnel. This is the default.
- *   - ircserver.cloakKey=none:           Don't cloak. Users may be correlated with their
- *                                        (probably) shared clients destination.
- *                                        Of course if the ircd does cloaking than this is ok.
  *   - ircserver.cloakKey=somepassphrase: Cloak with the hash of the passphrase. Use this to
  *                                        have consistent mangling across restarts, or to
  *                                        have multiple IRC servers cloak consistently to
  *                                        be able to track users even when they switch servers.
  *                                        Note: don't quote or put spaces in the passphrase,
  *                                        the i2ptunnel gui can't handle it.
+ *   - ircserver.fakeHostname=%f.b32.i2p: Set the fake hostname sent by I2PTunnel,
+ *                                        %f is the full B32 destination hash
+ *                                        %c is the cloaked hash.
  *
  * There is no outbound filtering.
  *
  * @author zzz
  */
 public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
-
+    public static final String PROP_CLOAK="ircserver.cloakKey";
+    public static final String PROP_HOSTNAME="ircserver.fakeHostname";
+    public static final String PROP_HOSTNAME_DEFAULT="%f.b32.i2p";
+    
     private static final Log _log = new Log(I2PTunnelIRCServer.class);
-    private static final String PROP_CLOAK="ircserver.cloakKey";
-    private boolean _cloak;
-    private byte[] _cloakKey; // 32 bytes of stuff to scramble the dest with
+    
     
     /**
      * @throws IllegalArgumentException if the I2PTunnel does not contain
@@ -71,15 +73,14 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
     private void initCloak(I2PTunnel tunnel) {
         Properties opts = tunnel.getClientOptions();
         String passphrase = opts.getProperty(PROP_CLOAK);
-        _cloak = passphrase == null || !"none".equals(passphrase);
-        if (_cloak) {
-            if (passphrase == null) {
-                _cloakKey = new byte[Hash.HASH_LENGTH];
-                tunnel.getContext().random().nextBytes(_cloakKey);
-            } else {
-                _cloakKey = SHA256Generator.getInstance().calculateHash(passphrase.trim().getBytes()).getData();
-            }
+        if (passphrase == null) {
+            this.cloakKey = new byte[Hash.HASH_LENGTH];
+            tunnel.getContext().random().nextBytes(this.cloakKey);
+        } else {
+            this.cloakKey = SHA256Generator.getInstance().calculateHash(passphrase.trim().getBytes()).getData();
         }
+        
+        this.hostname = opts.getProperty(PROP_HOSTNAME, PROP_HOSTNAME_DEFAULT);
     }
     
     protected void blockingHandle(I2PSocket socket) {
@@ -122,16 +123,17 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
      *
      */
     String cloakDest(Destination d) {
-        Hash h;
-        if (_cloak) {
-            byte[] b = new byte[d.size() + _cloakKey.length];
-            System.arraycopy(b, 0, d.toByteArray(), 0, d.size());
-            System.arraycopy(b, d.size(), _cloakKey, 0, _cloakKey.length);
-            h = SHA256Generator.getInstance().calculateHash(b);
-        } else {
-            h = d.calculateHash();
-        }
-        return h.toBase64().substring(0, 8) + ".i2p";
+        String hf;
+        String hc;
+        
+        byte[] b = new byte[d.size() + this.cloakKey.length];
+        System.arraycopy(b, 0, d.toByteArray(), 0, d.size());
+        System.arraycopy(b, d.size(), this.cloakKey, 0, this.cloakKey.length);
+        hc = Base32.encode(SHA256Generator.getInstance().calculateHash(b).getData());
+        
+        hf = Base32.encode(d.calculateHash().getData());
+        
+        return this.hostname.replace("%f", hf).replace("%c", hc);
     }
 
     /** keep reading until we see USER or SERVER */
@@ -156,9 +158,10 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
             if(field[0].charAt(0)==':')
                 idx++;
 
-            try { command = field[idx++]; }
-             catch (IndexOutOfBoundsException ioobe) // wtf, server sent borked command?
-            {
+            try {
+                command = field[idx++];
+            } catch (IndexOutOfBoundsException ioobe) {
+                // wtf, server sent borked command?
                throw new IOException("Dropping defective message: index out of bounds while extracting command.");
             }
 
@@ -169,7 +172,8 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
                 //  =>
                 // USER zzz1 abcd1234.i2p localhost :zzz
                 // this whole class is for these two lines...
-                buf.append("USER ").append(field[idx]).append(' ').append(newHostname).append(".i2p ");
+                buf.append("USER ").append(field[idx]).append(' ').append(newHostname);
+                buf.append(' ');
                 buf.append(field[idx+2]).append(' ').append(field[idx+3]).append("\r\n");
                 break;
             }
@@ -181,4 +185,7 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
             _log.debug("All done, sending: " + buf.toString());
         return buf.toString();
     }
+    
+    private byte[] cloakKey; // 32 bytes of stuff to scramble the dest with
+    private String hostname;
 }
diff --git a/core/java/src/net/i2p/data/PrivateKeyFile.java b/core/java/src/net/i2p/data/PrivateKeyFile.java
index d9e52aecc04325b9427cd27df7ccc9fd11aa0c35..b5d68ee41194f8eb27bb5c7926ad461517281985 100644
--- a/core/java/src/net/i2p/data/PrivateKeyFile.java
+++ b/core/java/src/net/i2p/data/PrivateKeyFile.java
@@ -261,7 +261,7 @@ public class PrivateKeyFile {
     public String toString() {
         StringBuffer s = new StringBuffer(128);
         s.append("Dest: ");
-        s.append(this.dest.toBase64());
+        s.append(this.dest != null ? this.dest.toBase64() : "null");
         s.append("\nContains: ");
         s.append(this.dest);
         s.append("\nPrivate Key: ");
diff --git a/history.txt b/history.txt
index 62c4430e9197ee1d30fbb0592be178e0792ecdc0..e5bfe64788e824c9130b5b5339d5b27e5d389be6 100644
--- a/history.txt
+++ b/history.txt
@@ -1,3 +1,54 @@
+2009-03-16 zzz
+    * help.jsp: Add some
+    * I2PTunnel: Cleanup
+    * I2PTunnelHTTPClient: Fix NPE on delayed open
+    * I2PTunnelHTTPServer: Maybe catch an NPE
+    * SOCKS: Allow .onion addresses for onioncat testing
+    * Tunnel: Catch a rare AIOOB
+
+2009-03-09 zzz
+    * Client:
+      - Clean up retry code
+      - Bring I2CP listen error to the summary bar
+        http://forum.i2p/viewtopic.php?t=3133
+    * I2PSnark: Remove the http from the add torrent box
+    * I2PTunnel:
+      - Add persistent key option for standard and IRC clients
+      - Add delay-open option for clients
+      - Get regenerate-dest-on-reconnect working
+      - Add default key file name
+      - Add link to addressbook
+      - I2PSink: Send protocol byte
+    * OCMOSJ:
+      - Change from 5% reply requests to at least
+        once per minute, in hopes of reducing IRC drops
+      - More clean up of the cache cleaning
+    * Routerconsole: Don't OOM configpeer.jsp on huge blocklists
+
+2009-02-26 zzz
+    * I2CP Client: Add support for muxing
+    * I2PTunnel:
+      - Add new IRCServer tunnel type
+      - Add SOCKS 4/4a support
+      - Catch OOMs in HTTPServer
+      - Name the IRCClient filter threads
+      - Port Streamr to I2PTunnel
+      - The beginnings of SOCKS UDP support
+    * Naming: Add reverse lookup by hash
+    * OCMOSJ: Clean up the cache cleaning
+    * Router: Move addShutdownTask from Router to I2PAppContext
+      so that apps can register more easily
+    * Routerconsole:
+      - Thread hard shutdown and restart requests from the routerconsole,
+        and add a delay even if no tunnels, to allow time for a UI response
+      - Sort the summary bar destinations
+      - Move dest-to-hash converter to new helper class so we can
+        use it in i2ptunnel
+
+2009-02-22 sponge
+    * BOB: Orphan tunnel issue fix, bump BOB version
+    * bump to Build 6
+
 2009-02-16 zzz
     * Streaming lib: Plug timer leak, don't send keepalives
       after close, don't disconnect hard after close
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index 5203f2360b8c639e5b7467177c7101dc9a20b67e..6ade2ee81032a9681f7555a074bd3834bd9d4d64 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 = CoreVersion.VERSION;
-    public final static long BUILD = 5;
+    public final static long BUILD = 9;
     public static void main(String args[]) {
         System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
         System.out.println("Router ID: " + RouterVersion.ID);