diff --git a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java
index 738e0c65e08534e8a2f7572c78cc04abbd751c2d..dc055bdbcccf142a4bb708010a0bf6dbf273d58a 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java
@@ -96,8 +96,8 @@ public class I2PSnarkUtil {
                 opts.setProperty("i2p.streaming.inactivityAction", "1");
             if (opts.getProperty("i2p.streaming.writeTimeout") == null)
                 opts.setProperty("i2p.streaming.writeTimeout", "90000");
-            if (opts.getProperty("i2p.streaming.readTimeout") == null)
-                opts.setProperty("i2p.streaming.readTimeout", "90000");
+            //if (opts.getProperty("i2p.streaming.readTimeout") == null)
+            //    opts.setProperty("i2p.streaming.readTimeout", "120000");
             _manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
         }
         return (_manager != null);
diff --git a/apps/i2psnark/java/src/org/klomp/snark/Snark.java b/apps/i2psnark/java/src/org/klomp/snark/Snark.java
index 4d95296021104ba33b75c49ba7bcaff021bd20f1..2e3d304f6d8032200b9c4b4fda0d137b1594bd35 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/Snark.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/Snark.java
@@ -411,15 +411,23 @@ public class Snark
    */
   public void stopTorrent() {
     stopped = true;
-    trackerclient.halt();
-    coordinator.halt();
-    try { 
-        storage.close(); 
-    } catch (IOException ioe) {
-        System.out.println("Error closing " + torrent);
-        ioe.printStackTrace();
+    TrackerClient tc = trackerclient;
+    if (tc != null)
+        tc.halt();
+    PeerCoordinator pc = coordinator;
+    if (pc != null)
+        pc.halt();
+    Storage st = storage;
+    if (st != null) {
+        try { 
+            storage.close(); 
+        } catch (IOException ioe) {
+            System.out.println("Error closing " + torrent);
+            ioe.printStackTrace();
+        }
     }
-    PeerCoordinatorSet.instance().remove(coordinator);
+    if (pc != null)
+        PeerCoordinatorSet.instance().remove(pc);
   }
 
   static Snark parseArguments(String[] args)
diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
index 6f292013102072f4b5a95245064979270a17c90d..56b5da3acaf50a988011fcae82a59e243b53d566 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
@@ -199,7 +199,9 @@ public class I2PSnarkServlet extends HttpServlet {
                             List files = snark.meta.getFiles();
                             String dataFile = snark.meta.getName();
                             for (int i = 0; files != null && i < files.size(); i++) {
-                                File df = new File(_manager.getDataDir(), (String)files.get(i));
+                                // multifile torrents have the getFiles() return lists of lists of filenames, but
+                                // each of those lists just contain a single file afaict...
+                                File df = new File(_manager.getDataDir(), files.get(i).toString());
                                 boolean deleted = FileUtil.rmdir(df, false);
                                 if (deleted)
                                     _manager.addMessage("Data dir deleted: " + df.getAbsolutePath());
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
index bcaee116f062e2da70ed3225d673dd6b89d0388a..4eb05ab3712af3a569cce55a061dc8417fde0392 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
@@ -110,6 +110,9 @@ public class SummaryHelper {
     public int getAllPeers() { return _context.netDb().getKnownRouters(); }
     
     public String getReachability() {
+        if (!_context.clock().getUpdatedSuccessfully())
+            return "ERR-ClockSkew";
+        
         int status = _context.commSystem().getReachabilityStatus();
         switch (status) {
             case CommSystemFacade.STATUS_OK:
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
index 98e6178d8e2ff3556522398c5c2d2b6dda8138e6..74b52900c2397fcd7fd00495b91acbe1095cb2f4 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
@@ -95,6 +95,8 @@ public class Connection {
         _outboundQueue = queue;
         _handler = handler;
         _options = (opts != null ? opts : new ConnectionOptions());
+        _outputStream.setWriteTimeout((int)_options.getWriteTimeout());
+        _inputStream.setReadTimeout((int)_options.getReadTimeout());
         _lastSendId = -1;
         _nextSendTime = -1;
         _ackedPackets = 0;
@@ -145,8 +147,8 @@ public class Connection {
      */
     boolean packetSendChoke(long timeoutMs) {
         if (false) return true;
-        long writeExpire = timeoutMs;
         long start = _context.clock().now();
+        long writeExpire = start + timeoutMs;
         boolean started = false;
         while (true) {
             long timeLeft = writeExpire - _context.clock().now();
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketFull.java b/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketFull.java
index 0daf0ba65473c995546b1241497ac29921a1125b..48aece1849835049064f579bd64bb45b60b06d47 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketFull.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketFull.java
@@ -90,6 +90,7 @@ public class I2PSocketFull implements I2PSocket {
         Connection c = _connection;
         if (c == null) return;
         
+        c.getInputStream().setReadTimeout((int)ms);
         c.getOptions().setReadTimeout(ms);
     }
     
@@ -106,8 +107,11 @@ public class I2PSocketFull implements I2PSocket {
     }
     
     void destroy() { 
+        Connection c = _connection;
         _connection = null; 
         _listener = null;
+        if (c != null)
+            c.disconnectComplete();
     }
     public String toString() {
         Connection c = _connection;
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/MessageOutputStream.java b/apps/streaming/java/src/net/i2p/client/streaming/MessageOutputStream.java
index 390cb93245e8f3293d80c22e3b869c29e8660d59..453733a2b30d6e6ee52efd5b5b02a23a4208026b 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/MessageOutputStream.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/MessageOutputStream.java
@@ -405,6 +405,10 @@ public class MessageOutputStream extends OutputStream {
     
     void destroy() {
         _dataReceiver = null;
+        synchronized (_dataLock) {
+            _closed = true;
+            _dataLock.notifyAll();
+        }
     }
     
     /** Define a component to receive data flushed from this stream */
diff --git a/core/java/src/net/i2p/util/Clock.java b/core/java/src/net/i2p/util/Clock.java
index e607b36b74496cbca335f997e946884b055c3b9b..4c1148acda94c80170c3e449af55050b701dd9ae 100644
--- a/core/java/src/net/i2p/util/Clock.java
+++ b/core/java/src/net/i2p/util/Clock.java
@@ -82,7 +82,11 @@ public class Clock implements Timestamper.UpdateListener {
             }
         }
         if (_alreadyChanged) {
-            getLog().log(Log.CRIT, "Updating clock offset to " + offsetMs + "ms from " + _offset + "ms");
+            if (delta > 15*1000)
+                getLog().log(Log.CRIT, "Updating clock offset to " + offsetMs + "ms from " + _offset + "ms");
+            else if (getLog().shouldLog(Log.INFO))
+                getLog().info("Updating clock offset to " + offsetMs + "ms from " + _offset + "ms");
+            
             if (!_statCreated)
                 _context.statManager().createRateStat("clock.skew", "How far is the already adjusted clock being skewed?", "Clock", new long[] { 10*60*1000, 3*60*60*1000, 24*60*60*60 });
                 _statCreated = true;
@@ -98,6 +102,8 @@ public class Clock implements Timestamper.UpdateListener {
     public long getOffset() {
         return _offset;
     }
+    
+    public boolean getUpdatedSuccessfully() { return _alreadyChanged; }
 
     public void setNow(long realTime) {
         long diff = realTime - System.currentTimeMillis();
diff --git a/history.txt b/history.txt
index c4d92b9297b657bcc275ddceface7384f40662fe..53e2d47ef3a05fde1e57da51a4af8e0dc92a57f9 100644
--- a/history.txt
+++ b/history.txt
@@ -1,4 +1,14 @@
-$Id: history.txt,v 1.369 2005/12/22 07:49:09 jrandom Exp $
+$Id: history.txt,v 1.370 2005/12/26 23:20:29 complication Exp $
+
+2005-12-27  jrandom
+    * Add a new Status: line on the router console - "ERR-ClockSkew", in case
+      the clock is too skewed to do anything useful (check the year and month,
+      not just the hour and minute).
+    * Fixed the read/write timeouts in the streaming lib (so that it actually
+      honors them now)
+    * Minor I2PSnark cleanups (no read timeout, more careful shutdown and
+      torrent closing)
+    * Handle an oddball tunnel creation failure (thanks Xunk)
 
 2005-12-26  Complication
     * Fix some integer typecasting in I2PSnark (caused >2GB torrents to fail)
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index f26fa5a75f817d4002cc489593bbf756de45635b..e3914c3bb39ec810a852cc67572af5463c9d5423 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
  *
  */
 public class RouterVersion {
-    public final static String ID = "$Revision: 1.317 $ $Date: 2005/12/22 07:49:09 $";
+    public final static String ID = "$Revision: 1.318 $ $Date: 2005/12/26 23:20:30 $";
     public final static String VERSION = "0.6.1.8";
-    public final static long BUILD = 1;
+    public final static long BUILD = 2;
     public static void main(String args[]) {
         System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
         System.out.println("Router ID: " + RouterVersion.ID);
diff --git a/router/java/src/net/i2p/router/tunnel/pool/RequestTunnelJob.java b/router/java/src/net/i2p/router/tunnel/pool/RequestTunnelJob.java
index 44f075ebf2cb0c8915ba6c59a4f40309623a8092..0b8186e251444f5fe6fdd1917aff2efce3c22f43 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/RequestTunnelJob.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/RequestTunnelJob.java
@@ -252,15 +252,17 @@ public class RequestTunnelJob extends JobImpl {
                 default:
                     // ignore
             }
-            
-            // penalize peer based on their bitchiness level
-            getContext().profileManager().tunnelRejected(_currentPeer.getIdentity().calculateHash(), 
-                                                         getContext().clock().now() - _lastSendTime,
-                                                         howBad);
+
+            if (_currentPeer != null)
+                // penalize peer based on their bitchiness level
+                getContext().profileManager().tunnelRejected(_currentPeer.getIdentity().calculateHash(), 
+                                                             getContext().clock().now() - _lastSendTime,
+                                                             howBad);
         }
         if (_log.shouldLog(Log.INFO))
             _log.info("Tunnel request failed w/ cause=" + howBad + " for peer " 
-                      + _currentPeer.getIdentity().calculateHash().toBase64().substring(0,4));
+                      + (_currentPeer == null ? "[unknown]" : 
+                         _currentPeer.getIdentity().calculateHash().toBase64().substring(0,4)));
         tunnelFail();
     }