diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java
index 807818d44bf621f0ee8659c6a2fba791ddee73d3..2ba6acc11ff377f13c8c8b114042febbde95573c 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java
@@ -263,6 +263,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
                     getTunnel().removeSession(sess);
                     if (_log.shouldLog(Log.WARN))
                         _log.warn(getTunnel().getClientOptions().getProperty("inbound.nickname") + ": Built a new destination on resume");
+                    // make sure the old one is closed
+                    sockMgr.destroySocketManager();
                     newManager = true;
                 }  // else the old socket manager will reconnect the old session if necessary
             }
@@ -320,6 +322,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
                 if (_log.shouldLog(Log.INFO))
                     _log.info(tunnel.getClientOptions().getProperty("inbound.nickname") + ": Building a new socket manager since the old one closed [s=" + s + "]");
                 tunnel.removeSession(s);
+                // make sure the old one is closed
+                socketManager.destroySocketManager();
                 // We could be here a LONG time, holding the lock
                 socketManager = buildSocketManager(tunnel, pkf);
             } else {
diff --git a/apps/routerconsole/jsp/old-translations/help_ar.jsp.old b/apps/routerconsole/jsp/help_ar.jsp
similarity index 100%
rename from apps/routerconsole/jsp/old-translations/help_ar.jsp.old
rename to apps/routerconsole/jsp/help_ar.jsp
diff --git a/apps/routerconsole/jsp/old-translations/help_fr.jsp.old b/apps/routerconsole/jsp/help_fr.jsp
similarity index 100%
rename from apps/routerconsole/jsp/old-translations/help_fr.jsp.old
rename to apps/routerconsole/jsp/help_fr.jsp
diff --git a/apps/routerconsole/jsp/old-translations/help_nl.jsp.old b/apps/routerconsole/jsp/help_nl.jsp
similarity index 100%
rename from apps/routerconsole/jsp/old-translations/help_nl.jsp.old
rename to apps/routerconsole/jsp/help_nl.jsp
diff --git a/apps/routerconsole/jsp/old-translations/help_ru.jsp.old b/apps/routerconsole/jsp/help_ru.jsp
similarity index 100%
rename from apps/routerconsole/jsp/old-translations/help_ru.jsp.old
rename to apps/routerconsole/jsp/help_ru.jsp
diff --git a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java
index 43e7efecc95643c9d8fa629831b11c60f16dc040..2b2a3f23c10e4fe62f2df14575cd9e79225387ea 100644
--- a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java
+++ b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java
@@ -1281,10 +1281,15 @@ public class WebMail extends HttpServlet
 			
 			if( sessionObject.state == STATE_SHOW ) {
 				processMessageButtons( sessionObject, request );
-				Mail mail = sessionObject.mailCache.getMail( sessionObject.showUIDL, MailCache.FETCH_ALL );
-				if( mail != null && mail.error.length() > 0 ) {
-					sessionObject.error += mail.error;
-					mail.error = "";
+				// If the last message has just been deleted then
+				// sessionObject.state = STATE_LIST and
+				// sessionObject.showUIDL = null
+				if ( sessionObject.showUIDL != null ) {
+					Mail mail = sessionObject.mailCache.getMail( sessionObject.showUIDL, MailCache.FETCH_ALL );
+					if( mail != null && mail.error.length() > 0 ) {
+						sessionObject.error += mail.error;
+						mail.error = "";
+					}
 				}
 			}
 			
diff --git a/history.txt b/history.txt
index ec0641461d9f8e3a8f7e4a3a8321e4feab7d0088..15e4be9ca75c14703df91731b84531be8e004638 100644
--- a/history.txt
+++ b/history.txt
@@ -1,5 +1,10 @@
-2013-11-19 kytv
+2013-11-23 zzz
+ * i2ptunnel: Clean up old timer threads
+
+2013-11-23 str4d
+ * susimail: Fix NPE when deleting last message (ticket #414)
 
+2013-11-19 kytv
  * Translation updates and start of Japanese translation pulled from Transifex
 
 2013-11-14 kytv
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index 5fd579e05b82a079d03afc83109a625c6e423097..a06babd5191f9210a1e6b3504e0117fddd4e472a 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -18,7 +18,7 @@ public class RouterVersion {
     /** deprecated */
     public final static String ID = "Monotone";
     public final static String VERSION = CoreVersion.VERSION;
-    public final static long BUILD = 30;
+    public final static long BUILD = 31;
 
     /** for example "-test" */
     public final static String EXTRA = "-rc";
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java
index edc186954c76fe78c98f13fde83127319a708324..868f0d0b880a9d2818566fe055b42a3db3e75bcb 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java
@@ -156,7 +156,7 @@ class IterativeSearchJob extends FloodSearchJob {
         _toTry.remove(_key);
         if (_toTry.isEmpty()) {
             if (_log.shouldLog(Log.WARN))
-                _log.warn(getJobId() + ": Iterative search for " + _key + " had no peers to send to");
+                _log.warn(getJobId() + ": ISJ for " + _key + " had no peers to send to");
             // no floodfill peers, fail
             failed();
             return;
@@ -168,7 +168,9 @@ class IterativeSearchJob extends FloodSearchJob {
         Job onTimeout = new FloodOnlyLookupTimeoutJob(getContext(), this);
         _out = getContext().messageRegistry().registerPending(replySelector, onReply, onTimeout, _timeoutMs);
         if (_log.shouldLog(Log.INFO))
-            _log.info(getJobId() + ": Iterative search for " + _key + " (rkey " + _rkey + ") timeout " +
+            _log.info(getJobId() + ": New ISJ for " +
+                      (_isLease ? "LS " : "RI ") +
+                      _key + " (rkey " + _rkey + ") timeout " +
                       DataHelper.formatDuration(_timeoutMs) + " toTry: "  + DataHelper.toString(_toTry));
         retry();
     }
@@ -251,8 +253,13 @@ class IterativeSearchJob extends FloodSearchJob {
             dlm.setReplyTunnel(replyTunnel.getReceiveTunnelId(0));
             dlm.setSearchKey(_key);
             
-            if (_log.shouldLog(Log.INFO))
-                _log.info(getJobId() + ": Iterative search for " + _key + " to " + peer);
+            if (_log.shouldLog(Log.INFO)) {
+                int tries;
+                synchronized(this) {
+                    tries = _unheardFrom.size() + _failedPeers.size();
+                }
+                _log.info(getJobId() + ": ISJ try " + tries + " for " + _key + " to " + peer);
+            }
             long now = getContext().clock().now();
             _sentTime.put(peer, Long.valueOf(now));
 
@@ -270,6 +277,13 @@ class IterativeSearchJob extends FloodSearchJob {
                         dlm.setReplySession(sess.key, sess.tag);
                     }
                     outMsg = MessageWrapper.wrap(getContext(), dlm, ri);
+                    // ElG can take a while so do a final check before we send it,
+                    // a response may have come in.
+                    if (_dead) {
+                        if (_log.shouldLog(Log.DEBUG))
+                            _log.debug(getJobId() + ": aborting send, finished while wrapping msg to " + peer);
+                        return;
+                    }
                     if (_log.shouldLog(Log.DEBUG))
                         _log.debug(getJobId() + ": Encrypted DLM for " + _key + " to " + peer);
                 }
@@ -375,7 +389,7 @@ class IterativeSearchJob extends FloodSearchJob {
         long time = System.currentTimeMillis() - _created;
         if (_log.shouldLog(Log.INFO)) {
             long timeRemaining = _expiration - getContext().clock().now();
-            _log.info(getJobId() + ": Iterative search for " + _key + " failed with " + timeRemaining + " remaining after " + time +
+            _log.info(getJobId() + ": ISJ for " + _key + " failed with " + timeRemaining + " remaining after " + time +
                       ", peers queried: " + tries);
         }
         getContext().statManager().addRateData("netDb.failedTime", time, 0);
@@ -410,7 +424,7 @@ class IterativeSearchJob extends FloodSearchJob {
         }
         long time = System.currentTimeMillis() - _created;
         if (_log.shouldLog(Log.INFO))
-            _log.info(getJobId() + ": Iterative search for " + _key + " successful after " + time +
+            _log.info(getJobId() + ": ISJ for " + _key + " successful after " + time +
                       ", peers queried: " + tries);
         getContext().statManager().addRateData("netDb.successTime", time, 0);
         getContext().statManager().addRateData("netDb.successRetries", tries - 1, 0);