From f11a5432338ff503c8e26a3817d1ed8fcda8b2cf Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Fri, 11 Feb 2011 23:59:10 +0000
Subject: [PATCH]     * Clock: Cleanups and javadocs     * EepGet: Cleanups and
 javadocs     * Reseed: Use the reseeder as a clock source

---
 .../i2p/client/streaming/I2PSocketEepGet.java |  1 -
 .../src/net/i2p/router/web/NewsFetcher.java   | 45 +-----------
 .../i2p/router/web/UnsignedUpdateHandler.java |  3 +-
 .../src/net/i2p/router/web/UpdateHandler.java |  3 +-
 core/java/src/net/i2p/time/Timestamper.java   | 18 ++---
 core/java/src/net/i2p/util/Clock.java         | 13 +++-
 core/java/src/net/i2p/util/EepGet.java        | 41 +++++------
 .../java/src/net/i2p/router/RouterClock.java  | 18 +++--
 .../i2p/router/networkdb/reseed/Reseeder.java | 70 ++++++++++++++++---
 .../transport/CommSystemFacadeImpl.java       |  2 +-
 .../router/transport/ntcp/EstablishState.java |  4 +-
 .../router/transport/udp/PacketHandler.java   |  2 +-
 .../src/net/i2p/router/util/RFC822Date.java   | 51 ++++++++++++++
 13 files changed, 169 insertions(+), 102 deletions(-)
 create mode 100644 router/java/src/net/i2p/router/util/RFC822Date.java

diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketEepGet.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketEepGet.java
index 834c52160c..d68270bc62 100644
--- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketEepGet.java
+++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketEepGet.java
@@ -59,7 +59,6 @@ public class I2PSocketEepGet extends EepGet {
         // public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, long minSize, long maxSize, String outputFile, OutputStream outputStream, String url, boolean allowCaching, String etag, String postData) {
         super(ctx, false, null, -1, numRetries, minSize, maxSize, outputFile, outputStream, url, true, null, null);
         _socketManager = mgr;
-        _log = ctx.logManager().getLog(I2PSocketEepGet.class);
     }
    
     /**
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java b/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java
index adb2380777..562c3e7cad 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java
@@ -3,11 +3,9 @@ package net.i2p.router.web;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
-import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.List;
-import java.util.Locale;
 
 import net.i2p.I2PAppContext;
 import net.i2p.crypto.TrustedUpdate;
@@ -15,6 +13,7 @@ import net.i2p.data.DataHelper;
 import net.i2p.router.Router;
 import net.i2p.router.RouterContext;
 import net.i2p.router.RouterVersion;
+import net.i2p.router.util.RFC822Date;
 import net.i2p.util.EepGet;
 import net.i2p.util.EepHead;
 import net.i2p.util.FileUtil;
@@ -73,7 +72,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
             if (_lastFetch == 0)
                 _lastFetch = _lastUpdated;
             if (_lastModified == null)
-                _lastModified = to822Date(_lastFetch);
+                _lastModified = RFC822Date.to822Date(_lastFetch);
         } else {
             _lastUpdated = 0;
             _lastFetch = 0;
@@ -212,7 +211,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
                 String lastmod = get.getLastModified();
                 if (lastmod != null) {
                     if (!(_context.isRouterContext())) return;
-                    long modtime = parse822Date(lastmod);
+                    long modtime = RFC822Date.parse822Date(lastmod);
                     if (modtime <= 0) return;
                     String lastUpdate = _context.getProperty(UpdateHandler.PROP_LAST_UPDATE_TIME);
                     if (lastUpdate == null) {
@@ -251,44 +250,6 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
         handler.update();
     }
 
-    /**
-     * http://jimyjoshi.com/blog/2007/08/rfc822dateparsinginjava.html
-     * Apparently public domain
-     * Probably don't need all of these...
-     */
-    private static final SimpleDateFormat rfc822DateFormats[] = new SimpleDateFormat[] {
-                 new SimpleDateFormat("EEE, d MMM yy HH:mm:ss z", Locale.US),
-                 new SimpleDateFormat("EEE, d MMM yy HH:mm z", Locale.US),
-                 new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.US),
-                 new SimpleDateFormat("EEE, d MMM yyyy HH:mm z", Locale.US),
-                 new SimpleDateFormat("d MMM yy HH:mm z", Locale.US),
-                 new SimpleDateFormat("d MMM yy HH:mm:ss z", Locale.US),
-                 new SimpleDateFormat("d MMM yyyy HH:mm z", Locale.US),
-                 new SimpleDateFormat("d MMM yyyy HH:mm:ss z", Locale.US)
-    };
-
-    /**
-     * new Date(String foo) is deprecated, so let's do this the hard way
-     *
-     * @param s non-null
-     * @return -1 on failure
-     */
-    public static long parse822Date(String s) {
-        for (int i = 0; i < rfc822DateFormats.length; i++) {
-            try {
-                Date date = rfc822DateFormats[i].parse(s);
-                if (date != null)
-                    return date.getTime();
-            } catch (ParseException pe) {}
-        }
-        return -1;
-    }
-
-    /** @since 0.8.2 */
-    private static String to822Date(long t) {
-        return (new SimpleDateFormat("d MMM yyyy HH:mm:ss z", Locale.US)).format(new Date(t));
-    }
-
     private static final String VERSION_STRING = "version=\"" + RouterVersion.VERSION + "\"";
     private static final String VERSION_PREFIX = "version=\"";
     private void checkForUpdates() {
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/UnsignedUpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/UnsignedUpdateHandler.java
index 8fcbd65c0b..5a52ee883a 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/UnsignedUpdateHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/UnsignedUpdateHandler.java
@@ -4,6 +4,7 @@ import java.io.File;
 
 import net.i2p.router.Router;
 import net.i2p.router.RouterContext;
+import net.i2p.router.util.RFC822Date;
 import net.i2p.util.EepGet;
 import net.i2p.util.FileUtil;
 import net.i2p.util.I2PAppThread;
@@ -101,7 +102,7 @@ public class UnsignedUpdateHandler extends UpdateHandler {
                 String lastmod = _get.getLastModified();
                 long modtime = 0;
                 if (lastmod != null)
-                    modtime = NewsFetcher.parse822Date(lastmod);
+                    modtime = RFC822Date.parse822Date(lastmod);
                 if (modtime <= 0)
                     modtime = _context.clock().now();
                 _context.router().setConfigSetting(PROP_LAST_UPDATE_TIME, "" + modtime);
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java
index 174704ba67..423b9e52ad 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java
@@ -15,6 +15,7 @@ import net.i2p.data.DataHelper;
 import net.i2p.router.Router;
 import net.i2p.router.RouterContext;
 import net.i2p.router.RouterVersion;
+import net.i2p.router.util.RFC822Date;
 import net.i2p.util.EepGet;
 import net.i2p.util.I2PAppThread;
 import net.i2p.util.Log;
@@ -271,7 +272,7 @@ public class UpdateHandler {
                 String lastmod = _get.getLastModified();
                 long modtime = 0;
                 if (lastmod != null)
-                    modtime = NewsFetcher.parse822Date(lastmod);
+                    modtime = RFC822Date.parse822Date(lastmod);
                 if (modtime <= 0)
                     modtime = _context.clock().now();
                 _context.router().setConfigSetting(PROP_LAST_UPDATE_TIME, "" + modtime);
diff --git a/core/java/src/net/i2p/time/Timestamper.java b/core/java/src/net/i2p/time/Timestamper.java
index a84882215c..2c798bab6b 100644
--- a/core/java/src/net/i2p/time/Timestamper.java
+++ b/core/java/src/net/i2p/time/Timestamper.java
@@ -4,6 +4,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 import java.util.StringTokenizer;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import net.i2p.I2PAppContext;
 import net.i2p.util.I2PThread;
@@ -56,7 +57,7 @@ public class Timestamper implements Runnable {
     public Timestamper(I2PAppContext ctx, UpdateListener lsnr, boolean daemon) {
         // moved here to prevent problems with synchronized statements.
         _servers = new ArrayList(3);
-        _listeners = new ArrayList(1);
+        _listeners = new CopyOnWriteArrayList();
         // Don't bother starting a thread if we are disabled.
         // This means we no longer check every 5 minutes to see if we got enabled,
         // so the property must be set at startup.
@@ -92,24 +93,16 @@ public class Timestamper implements Runnable {
     public boolean getIsDisabled() { return _disabled; }
     
     public void addListener(UpdateListener lsnr) {
-        synchronized (_listeners) {
             _listeners.add(lsnr);
-        }
     }
     public void removeListener(UpdateListener lsnr) {
-        synchronized (_listeners) {
             _listeners.remove(lsnr);
-        }
     }
     public int getListenerCount() {
-        synchronized (_listeners) {
             return _listeners.size();
-        }
     }
     public UpdateListener getListener(int index) {
-        synchronized (_listeners) {
             return _listeners.get(index);
-        }
     }
     
     private void startTimestamper() {
@@ -257,11 +250,8 @@ public class Timestamper implements Runnable {
      */
     private void stampTime(long now, int stratum) {
         long before = _context.clock().now();
-        synchronized (_listeners) {
-            for (int i = 0; i < _listeners.size(); i++) {
-                UpdateListener lsnr = _listeners.get(i);
-                lsnr.setNow(now, stratum);
-            }
+        for (UpdateListener lsnr : _listeners) {
+             lsnr.setNow(now, stratum);
         }
         if (_log.shouldLog(Log.DEBUG))
             _log.debug("Stamped the time as " + now + " (delta=" + (now-before) + ")");
diff --git a/core/java/src/net/i2p/util/Clock.java b/core/java/src/net/i2p/util/Clock.java
index 80696fc957..ee43af44cc 100644
--- a/core/java/src/net/i2p/util/Clock.java
+++ b/core/java/src/net/i2p/util/Clock.java
@@ -49,14 +49,22 @@ public class Clock implements Timestamper.UpdateListener {
     /** if the clock skewed changes by less than this, ignore the update (so we don't slide all over the place) */
     public final static long MIN_OFFSET_CHANGE = 5 * 1000;
 
+    /**
+     * Specify how far away from the "correct" time the computer is - a positive
+     * value means that the system time is slow, while a negative value means the system time is fast.
+     *
+     * @param offsetMs the delta from System.currentTimeMillis() (NOT the delta from now())
+     */
     public void setOffset(long offsetMs) {
         setOffset(offsetMs, false);        
     }
     
     /**
      * Specify how far away from the "correct" time the computer is - a positive
-     * value means that we are slow, while a negative value means we are fast.
+     * value means that the system time is slow, while a negative value means the system time is fast.
      * Warning - overridden in RouterClock
+     *
+     * @param offsetMs the delta from System.currentTimeMillis() (NOT the delta from now())
      */
     public void setOffset(long offsetMs, boolean force) {
         if (false) return;
@@ -101,6 +109,9 @@ public class Clock implements Timestamper.UpdateListener {
         fireOffsetChanged(delta);
     }
 
+    /*
+     * @return the current delta from System.currentTimeMillis() in milliseconds
+     */
     public long getOffset() {
         return _offset;
     }
diff --git a/core/java/src/net/i2p/util/EepGet.java b/core/java/src/net/i2p/util/EepGet.java
index 3ad84e6bbf..24155e59e7 100644
--- a/core/java/src/net/i2p/util/EepGet.java
+++ b/core/java/src/net/i2p/util/EepGet.java
@@ -28,21 +28,21 @@ import net.i2p.util.InternalSocket;
  * Bug: a malformed url http://example.i2p (no trailing '/') fails cryptically
  */
 public class EepGet {
-    protected I2PAppContext _context;
-    protected Log _log;
-    protected boolean _shouldProxy;
-    private String _proxyHost;
-    private int _proxyPort;
-    protected int _numRetries;
-    private long _minSize; // minimum and maximum acceptable response size, -1 signifies unlimited,
-    private long _maxSize; // applied both against whole responses and chunks
-    protected String _outputFile;
-    protected OutputStream _outputStream;
+    protected final I2PAppContext _context;
+    protected final Log _log;
+    protected final boolean _shouldProxy;
+    private final String _proxyHost;
+    private final int _proxyPort;
+    protected final int _numRetries;
+    private final long _minSize; // minimum and maximum acceptable response size, -1 signifies unlimited,
+    private final long _maxSize; // applied both against whole responses and chunks
+    protected final String _outputFile;
+    protected final OutputStream _outputStream;
     /** url we were asked to fetch */
-    protected String _url;
+    protected final String _url;
     /** the URL we actually fetch from (may differ from the _url in case of redirect) */
     protected String _actualURL;
-    private String _postData;
+    private final String _postData;
     private boolean _allowCaching;
     protected final List<StatusListener> _listeners;
     
@@ -106,7 +106,7 @@ public class EepGet {
                   String outputFile, OutputStream outputStream, String url, boolean allowCaching,
                   String etag, String lastModified, String postData) {
         _context = ctx;
-        _log = ctx.logManager().getLog(EepGet.class);
+        _log = ctx.logManager().getLog(getClass());
         _shouldProxy = (proxyHost != null) && (proxyHost.length() > 0) && (proxyPort > 0) && shouldProxy;
         _proxyHost = proxyHost;
         _proxyPort = proxyPort;
@@ -118,13 +118,7 @@ public class EepGet {
         _url = url;
         _actualURL = url;
         _postData = postData;
-        _alreadyTransferred = 0;
-        _bytesTransferred = 0;
         _bytesRemaining = -1;
-        _currentAttempt = 0;
-        _transferFailed = false;
-        _headersRead = false;
-        _aborted = false;
         _fetchHeaderTimeout = CONNECT_TIMEOUT;
         _listeners = new ArrayList(1);
         _etag = etag;
@@ -255,9 +249,9 @@ public class EepGet {
         public void attempting(String url);
     }
     protected class CLIStatusListener implements StatusListener {
-        private int _markSize;
-        private int _lineSize;
-        private long _startedOn;
+        private final int _markSize;
+        private final int _lineSize;
+        private final long _startedOn;
         private long _written;
         private long _previousWritten;
         private long _discarded;
@@ -271,9 +265,6 @@ public class EepGet {
         public CLIStatusListener(int markSize, int lineSize) { 
             _markSize = markSize;
             _lineSize = lineSize;
-            _written = 0;
-            _previousWritten = 0;
-            _discarded = 0;
             _lastComplete = _context.clock().now();
             _startedOn = _lastComplete;
             _firstTime = true;
diff --git a/router/java/src/net/i2p/router/RouterClock.java b/router/java/src/net/i2p/router/RouterClock.java
index fd15d7db38..3f7b629606 100644
--- a/router/java/src/net/i2p/router/RouterClock.java
+++ b/router/java/src/net/i2p/router/RouterClock.java
@@ -24,7 +24,7 @@ public class RouterClock extends Clock {
      *  All of this is @since 0.7.12
      */
     private static final long MAX_SLEW = 50;
-    private static final int DEFAULT_STRATUM = 8;
+    public static final int DEFAULT_STRATUM = 8;
     private static final int WORST_STRATUM = 16;
     /** the max NTP Timestamper delay is 30m right now, make this longer than that */
     private static final long MIN_DELAY_FOR_WORSE_STRATUM = 45*60*1000;
@@ -44,20 +44,27 @@ public class RouterClock extends Clock {
 
     /**
      * Specify how far away from the "correct" time the computer is - a positive
-     * value means that we are slow, while a negative value means we are fast.
+     * value means that the system time is slow, while a negative value means the system time is fast.
      *
+     * @param offsetMs the delta from System.currentTimeMillis() (NOT the delta from now())
      */
     @Override
     public void setOffset(long offsetMs, boolean force) {
          setOffset(offsetMs, force, DEFAULT_STRATUM);
     }
 
-    /** @since 0.7.12 */
+    /**
+     * @since 0.7.12
+     * @param offsetMs the delta from System.currentTimeMillis() (NOT the delta from now())
+     */
     private void setOffset(long offsetMs, int stratum) {
          setOffset(offsetMs, false, stratum);
     }
 
-    /** @since 0.7.12 */
+    /**
+     * @since 0.7.12
+     * @param offsetMs the delta from System.currentTimeMillis() (NOT the delta from now())
+     */
     private void setOffset(long offsetMs, boolean force, int stratum) {
         long delta = offsetMs - _offset;
         if (!force) {
@@ -91,7 +98,7 @@ public class RouterClock extends Clock {
             }
             
             // If so configured, check sanity of proposed clock offset
-            if (Boolean.valueOf(_contextRC.getProperty("router.clockOffsetSanityCheck","true")).booleanValue() &&
+            if (_contextRC.getBooleanPropertyDefaultTrue("router.clockOffsetSanityCheck") &&
                 _alreadyChanged) {
 
                 // Try calculating peer clock skew
@@ -192,6 +199,7 @@ public class RouterClock extends Clock {
     /*
      *  How far we still have to slew, for diagnostics
      *  @since 0.7.12
+     *  @deprecated for debugging only
      */
     public long getDeltaOffset() {
         return _desiredOffset - _offset;
diff --git a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java
index 4c4c2ce91f..93933aa51c 100644
--- a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java
+++ b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java
@@ -14,7 +14,10 @@ import java.util.Set;
 import java.util.StringTokenizer;
 
 import net.i2p.I2PAppContext;
+import net.i2p.data.DataHelper;
+import net.i2p.router.RouterClock;
 import net.i2p.router.RouterContext;
+import net.i2p.router.util.RFC822Date;
 import net.i2p.util.EepGet;
 import net.i2p.util.I2PAppThread;
 import net.i2p.util.Log;
@@ -34,6 +37,7 @@ import net.i2p.util.Translate;
  * the router log, and the wrapper log.
  */
 public class Reseeder {
+    /** FIXME don't keep a static reference, store _isRunning some other way */
     private static ReseedRunner _reseedRunner;
     private final RouterContext _context;
     private final Log _log;
@@ -46,6 +50,10 @@ public class Reseeder {
     /**
      *  NOTE - URLs in both the standard and SSL groups should use the same hostname and path,
      *         so the reseed process will not download from both.
+     *
+     *  NOTE - Each seedURL must be a directory, it must end with a '/',
+     *         it can't end with 'index.html', for example. Both because of how individual file
+     *         URLs are constructed, and because SSLEepGet doesn't follow redirects.
      */
     public static final String DEFAULT_SEED_URL =
               "http://a.netdb.i2p2.de/,http://c.netdb.i2p2.de/," +
@@ -98,13 +106,13 @@ public class Reseeder {
         private String _proxyHost;
         private int _proxyPort;
         private SSLEepGet.SSLState _sslState;
+        private int _gotDate;
+        private long _attemptStarted;
+        private static final int MAX_DATE_SETS = 2;
 
         public ReseedRunner() {
-            _isRunning = false; 
-            System.clearProperty(PROP_ERROR);
-            System.setProperty(PROP_STATUS, _("Reseeding"));
-            System.setProperty(PROP_INPROGRESS, "true");
         }
+
         public boolean isRunning() { return _isRunning; }
 
         /*
@@ -113,6 +121,11 @@ public class Reseeder {
          */
         public void run() {
             _isRunning = true;
+            System.clearProperty(PROP_ERROR);
+            System.setProperty(PROP_STATUS, _("Reseeding"));
+            System.setProperty(PROP_INPROGRESS, "true");
+            _attemptStarted = 0;
+            _gotDate = 0;
             _sslState = null;  // start fresh
             if (_context.getBooleanProperty(PROP_PROXY_ENABLE)) {
                 _proxyHost = _context.getProperty(PROP_PROXY_HOST);
@@ -152,8 +165,48 @@ public class Reseeder {
         public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {}
         public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {}
         public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {}
-        public void headerReceived(String url, int attemptNum, String key, String val) {}
-        public void attempting(String url) {}
+
+        /**
+         *  Use the Date header as a backup time source
+         */
+        public void headerReceived(String url, int attemptNum, String key, String val) {
+            // We do this more than once, because
+            // the first SSL handshake may take a while, and it may take the server
+            // a while to render the index page.
+            if (_gotDate < MAX_DATE_SETS && "date".equalsIgnoreCase(key) && _attemptStarted > 0) {
+                long timeRcvd = System.currentTimeMillis();
+                long serverTime = RFC822Date.parse822Date(val);
+                if (serverTime > 0) {
+                    // add 500ms since it's 1-sec resolution, and add half the RTT
+                    long now = serverTime + 500 + ((timeRcvd - _attemptStarted) / 2);
+                    long offset = now - _context.clock().now();
+                    if (_context.clock().getUpdatedSuccessfully()) {
+                        // 2nd time better than the first
+                        if (_gotDate > 0)
+                            _context.clock().setNow(now, RouterClock.DEFAULT_STRATUM - 2);
+                        else
+                            _context.clock().setNow(now, RouterClock.DEFAULT_STRATUM - 1);
+                        if (_log.shouldLog(Log.WARN))
+                            _log.warn("Reseed adjusting clock by " +
+                                      DataHelper.formatDuration(Math.abs(offset)));
+                    } else {
+                        // No peers or NTP yet, this is probably better than the peer average will be for a while
+                        // default stratum - 1, so the peer average is a worse stratum
+                        _context.clock().setNow(now, RouterClock.DEFAULT_STRATUM - 1);
+                        _log.logAlways(Log.WARN, "NTP failure, Reseed adjusting clock by " +
+                                                 DataHelper.formatDuration(Math.abs(offset)));
+                    }
+                    _gotDate++;
+                }
+            }
+        }
+
+        /** save the start time */
+        public void attempting(String url) {
+            if (_gotDate < MAX_DATE_SETS)
+                _attemptStarted = System.currentTimeMillis();
+        }
+
         // End of EepGet status listeners
 
         /**
@@ -235,7 +288,8 @@ public class Reseeder {
          **/
         private int reseedOne(String seedURL, boolean echoStatus) {
             try {
-                final long timeLimit = _context.clock().now() + MAX_TIME_PER_HOST;
+                // Don't use context clock as we may be adjusting the time
+                final long timeLimit = System.currentTimeMillis() + MAX_TIME_PER_HOST;
                 System.setProperty(PROP_STATUS, _("Reseeding: fetching seed URL."));
                 System.err.println("Reseeding from " + seedURL);
                 URL dir = new URL(seedURL);
@@ -275,7 +329,7 @@ public class Reseeder {
                 int errors = 0;
                 // 200 max from one URL
                 for (Iterator<String> iter = urlList.iterator();
-                     iter.hasNext() && fetched < 200 && _context.clock().now() < timeLimit; ) {
+                     iter.hasNext() && fetched < 200 && System.currentTimeMillis() < timeLimit; ) {
                     try {
                         System.setProperty(PROP_STATUS,
                             _("Reseeding: fetching router info from seed URL ({0} successful, {1} errors).", fetched, errors));
diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java
index d7849d5320..780a25d6b2 100644
--- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java
+++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java
@@ -509,7 +509,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
         buf.append("<tt>");
         boolean found = _context.netDb().lookupRouterInfoLocally(peer) != null;
         if (found)
-            buf.append("<a title=\"").append(_("NetDb entry")).append("\" href=\"netdb.jsp?r=").append(h).append("\">");
+            buf.append("<a title=\"").append(_("NetDb entry")).append("\" href=\"netdb?r=").append(h).append("\">");
         buf.append(h);
         if (found)
             buf.append("</a>");
diff --git a/router/java/src/net/i2p/router/transport/ntcp/EstablishState.java b/router/java/src/net/i2p/router/transport/ntcp/EstablishState.java
index d6109c6406..3dfc1b8934 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/EstablishState.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/EstablishState.java
@@ -399,7 +399,7 @@ class EstablishState {
                     _context.clock().setOffset(1000 * (_tsB - _tsA), true);
                     _tsA = _tsB;
                     if (diff != 0)
-                        _log.error("NTP failure, NTCP adjusting clock by " + DataHelper.formatDuration(diff));
+                        _log.logAlways(Log.WARN, "NTP failure, NTCP adjusting clock by " + DataHelper.formatDuration(diff));
                 } else if (diff >= Router.CLOCK_FUDGE_FACTOR) {
                     _context.statManager().addRateData("ntcp.invalidOutboundSkew", diff, 0);
                     _transport.markReachable(_con.getRemotePeer().calculateHash(), false);
@@ -617,7 +617,7 @@ class EstablishState {
                     _context.clock().setOffset(1000 * (_tsB - tsA), true);
                     tsA = _tsB;
                     if (diff != 0)
-                        _log.error("NTP failure, NTCP adjusting clock by " + DataHelper.formatDuration(diff));
+                        _log.logAlways(Log.WARN, "NTP failure, NTCP adjusting clock by " + DataHelper.formatDuration(diff));
                 } else if (diff >= Router.CLOCK_FUDGE_FACTOR) {
                     _context.statManager().addRateData("ntcp.invalidInboundSkew", diff, 0);
                     _transport.markReachable(alice.calculateHash(), true);
diff --git a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java
index 230c1b7921..ffbcf48aca 100644
--- a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java
+++ b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java
@@ -473,7 +473,7 @@ class PacketHandler {
                 // so we have to wait for NTCP to do it
                 _context.clock().setOffset(0 - skew, true);
                 if (skew != 0)
-                    _log.error("NTP failure, UDP adjusting clock by " + DataHelper.formatDuration(Math.abs(skew)));
+                    _log.logAlways(Log.WARN, "NTP failure, UDP adjusting clock by " + DataHelper.formatDuration(Math.abs(skew)));
             }
 
             if (skew > GRACE_PERIOD) {
diff --git a/router/java/src/net/i2p/router/util/RFC822Date.java b/router/java/src/net/i2p/router/util/RFC822Date.java
new file mode 100644
index 0000000000..6635bca8cf
--- /dev/null
+++ b/router/java/src/net/i2p/router/util/RFC822Date.java
@@ -0,0 +1,51 @@
+package net.i2p.router.util;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ *  Moved from NewsFetcher
+ *  @since 0.8.5
+ */
+public abstract class RFC822Date {
+
+    /**
+     * http://jimyjoshi.com/blog/2007/08/rfc822dateparsinginjava.html
+     * Apparently public domain
+     * Probably don't need all of these...
+     */
+    private static final SimpleDateFormat rfc822DateFormats[] = new SimpleDateFormat[] {
+                 new SimpleDateFormat("EEE, d MMM yy HH:mm:ss z", Locale.US),
+                 new SimpleDateFormat("EEE, d MMM yy HH:mm z", Locale.US),
+                 new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.US),
+                 new SimpleDateFormat("EEE, d MMM yyyy HH:mm z", Locale.US),
+                 new SimpleDateFormat("d MMM yy HH:mm z", Locale.US),
+                 new SimpleDateFormat("d MMM yy HH:mm:ss z", Locale.US),
+                 new SimpleDateFormat("d MMM yyyy HH:mm z", Locale.US),
+                 new SimpleDateFormat("d MMM yyyy HH:mm:ss z", Locale.US)
+    };
+
+    /**
+     * new Date(String foo) is deprecated, so let's do this the hard way
+     *
+     * @param s non-null
+     * @return -1 on failure
+     */
+    public static long parse822Date(String s) {
+        for (int i = 0; i < rfc822DateFormats.length; i++) {
+            try {
+                Date date = rfc822DateFormats[i].parse(s);
+                if (date != null)
+                    return date.getTime();
+            } catch (ParseException pe) {}
+        }
+        return -1;
+    }
+
+    /** @since 0.8.2 */
+    public static String to822Date(long t) {
+        return (new SimpleDateFormat("d MMM yyyy HH:mm:ss z", Locale.US)).format(new Date(t));
+    }
+}
-- 
GitLab