From 6331cb2374e113c5a994245cd03dd5225b11f380 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Sun, 21 Oct 2012 03:13:31 +0000
Subject: [PATCH] stub of a torrent updater

---
 .../src/org/klomp/snark/SnarkManager.java     |  29 +++-
 .../src/org/klomp/snark/UpdateHandler.java    |  54 +++++++
 .../src/org/klomp/snark/UpdateRunner.java     | 149 ++++++++++++++++++
 3 files changed, 228 insertions(+), 4 deletions(-)
 create mode 100644 apps/i2psnark/java/src/org/klomp/snark/UpdateHandler.java
 create mode 100644 apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java

diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
index a0883c05ed..8bae58b52b 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
@@ -889,7 +889,27 @@ public class SnarkManager implements CompleteListener {
      * @since 0.8.4
      */
     public void addMagnet(String name, byte[] ih, String trackerURL, boolean updateStatus) {
-        Snark torrent = new Snark(_util, name, ih, trackerURL, this,
+        addMagnet(name, ih, trackerURL, updateStatus, shouldAutoStart(), this);
+    }
+    
+    /**
+     * Add a torrent with the info hash alone (magnet / maggot)
+     * External use is for UpdateRunner.
+     *
+     * @param name hex or b32 name from the magnet link
+     * @param ih 20 byte info hash
+     * @param trackerURL may be null
+     * @param updateStatus should we add this magnet to the config file,
+     *                     to save it across restarts, in case we don't get
+     *                     the metadata before shutdown?
+     * @param listener to intercept callbacks, should pass through to this
+     * @return the new Snark or null on failure
+     * @throws RuntimeException via Snark.fatal()
+     * @since 0.9.4
+     */
+    public Snark addMagnet(String name, byte[] ih, String trackerURL, boolean updateStatus,
+                          boolean autoStart, CompleteListener listener) {
+        Snark torrent = new Snark(_util, name, ih, trackerURL, listener,
                                   _peerCoordinatorSet, _connectionAcceptor,
                                   false, getDataDir().getPath());
 
@@ -897,7 +917,7 @@ public class SnarkManager implements CompleteListener {
             Snark snark = getTorrentByInfoHash(ih);
             if (snark != null) {
                 addMessage(_("Torrent with this info hash is already running: {0}", snark.getBaseName()));
-                return;
+                return null;
             }
             // Tell the dir monitor not to delete us
             _magnets.add(name);
@@ -905,7 +925,7 @@ public class SnarkManager implements CompleteListener {
                 saveMagnetStatus(ih);
             _snarks.put(name, torrent);
         }
-        if (shouldAutoStart()) {
+        if (autoStart) {
             torrent.startTorrent();
             addMessage(_("Fetching {0}", name));
             DHT dht = _util.getDHT();
@@ -918,7 +938,8 @@ public class SnarkManager implements CompleteListener {
             }
         } else {
             addMessage(_("Adding {0}", name));
-      }
+        }
+        return torrent;
     }
 
     /**
diff --git a/apps/i2psnark/java/src/org/klomp/snark/UpdateHandler.java b/apps/i2psnark/java/src/org/klomp/snark/UpdateHandler.java
new file mode 100644
index 0000000000..0637c19a96
--- /dev/null
+++ b/apps/i2psnark/java/src/org/klomp/snark/UpdateHandler.java
@@ -0,0 +1,54 @@
+package org.klomp.snark;
+
+import java.net.URI;
+import java.util.List;
+
+import net.i2p.I2PAppContext;
+import net.i2p.update.*;
+
+/**
+ * <p>Handles the request to update the router by firing up a magnet.
+ * {@link net.i2p.util.EepGet} calls to download the latest signed update file
+ * and displaying the status to anyone who asks.
+ * </p>
+ * <p>After the download completes the signed update file is verified with
+ * {@link net.i2p.crypto.TrustedUpdate}, and if it's authentic the payload
+ * of the signed update file is unpacked and the router is restarted to complete
+ * the update process.
+ * </p>
+ *
+ * This does not do any checking, that is handled by the NewsFetcher.
+ *
+ * @since 0.9.4
+ */
+public class UpdateHandler implements Updater {
+    private final I2PAppContext _context;
+    private final UpdateManager _umgr;
+    private final SnarkManager _smgr;
+    
+    public UpdateHandler(I2PAppContext ctx, UpdateManager umgr, SnarkManager smgr) {
+        _context = ctx;
+        _umgr = umgr;
+        _smgr = smgr;
+    }
+    
+    /**
+     *  Start a download and return a handle to the download task.
+     *  Should not block.
+     *
+     *  @param id plugin name or ignored
+     *  @param maxTime how long you have
+     *  @return active task or null if unable to download
+     */
+    public UpdateTask update(UpdateType type, UpdateMethod method, List<URI> updateSources,
+                             String id, String newVersion, long maxTime) {
+        if (type != UpdateType.ROUTER_SIGNED ||
+            method != UpdateMethod.TORRENT || updateSources.isEmpty())
+            return null;
+        UpdateRunner update = new UpdateRunner(_context, _umgr, _smgr, updateSources, newVersion);
+        // set status before thread to ensure UI feedback
+        _umgr.notifyProgress(update, "<b>" + _smgr.util().getString("Updating") + "</b>");
+        update.start();
+        return update;
+    }
+}
diff --git a/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java b/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java
new file mode 100644
index 0000000000..53e0ec0f56
--- /dev/null
+++ b/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java
@@ -0,0 +1,149 @@
+package org.klomp.snark;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.net.URI;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import net.i2p.I2PAppContext;
+import net.i2p.crypto.TrustedUpdate;
+import net.i2p.data.DataHelper;
+import net.i2p.update.*;
+import net.i2p.util.Log;
+import net.i2p.util.VersionComparator;
+
+/**
+ *  The downloader for router signed updates.
+ *
+ *  @since 0.9.4
+ */
+class UpdateRunner implements UpdateTask, CompleteListener {
+    private final I2PAppContext _context;
+    private final Log _log;
+    private final UpdateManager _umgr;
+    private final SnarkManager _smgr;
+    private final List<URI> _urls;
+    private final String _updateFile;
+    private volatile boolean _isRunning;
+    private final String _newVersion;
+    private ByteArrayOutputStream _baos;
+    private URI _currentURI;
+    private Snark _snark;
+
+    private static final long CONNECT_TIMEOUT = 55*1000;
+    private static final long INACTIVITY_TIMEOUT = 5*60*1000;
+    private static final long NOPROXY_INACTIVITY_TIMEOUT = 60*1000;
+
+    public UpdateRunner(I2PAppContext ctx, UpdateManager umgr, SnarkManager smgr,
+                        List<URI> uris, String newVersion) { 
+        _context = ctx;
+        _log = ctx.logManager().getLog(getClass());
+        _umgr = umgr;
+        _smgr = smgr;
+        _urls = uris;
+        _newVersion = newVersion;
+        _updateFile = (new File(ctx.getTempDir(), "update" + ctx.random().nextInt() + ".tmp")).getAbsolutePath();
+    }
+
+    //////// begin UpdateTask methods
+
+    public boolean isRunning() { return _isRunning; }
+
+    public void shutdown() {
+        _isRunning = false;
+        if (_snark != null) {
+
+        }
+    }
+
+    public UpdateType getType() { return UpdateType.ROUTER_SIGNED; }
+
+    public UpdateMethod getMethod() { return UpdateMethod.TORRENT; }
+
+    public URI getURI() { return _currentURI; }
+
+    public String getID() { return ""; }
+
+    //////// end UpdateTask methods
+
+    public void start() {
+        _isRunning = true;
+        update();
+    }
+
+    /**
+     *  Loop through the entire list of update URLs.
+     *  For each one, first get the version from the first 56 bytes and see if
+     *  it is newer than what we are running now.
+     *  If it is, get the whole thing.
+     */
+    private void update() {
+
+        if (_urls.isEmpty()) {
+            _umgr.notifyTaskFailed(this, "", null);
+            return;
+        }
+
+        for (URI uri : _urls) {
+            _currentURI = uri;
+            String updateURL = uri.toString();
+            try {
+                MagnetURI magnet = new MagnetURI(_smgr.util(), updateURL);
+                String name = magnet.getName();
+                byte[] ih = magnet.getInfoHash();
+                String trackerURL = magnet.getTrackerURL();
+                _snark = _smgr.addMagnet(name, ih, trackerURL, true, true, this);
+                if (_snark != null) {
+                    updateStatus("<b>" + _smgr.util().getString("Updating from {0}", updateURL) + "</b>");
+                    break;
+                }
+            } catch (IllegalArgumentException iae) {}
+        }
+        if (_snark == null) {
+            _umgr.notifyTaskFailed(this, "", null);
+            _isRunning = false;
+        }
+    }
+
+    //////// begin CompleteListener methods
+    //////// all pass through to SnarkManager
+
+    public void torrentComplete(Snark snark) {
+
+        _smgr.torrentComplete(snark);
+    }
+
+    public void updateStatus(Snark snark) {
+
+        _smgr.updateStatus(snark);
+    }
+
+    public String gotMetaInfo(Snark snark) {
+
+        return _smgr.gotMetaInfo(snark);
+    }
+
+    public void fatal(Snark snark, String error) {
+        _smgr.fatal(snark, error);
+    }
+
+    public void addMessage(Snark snark, String message) {
+        _smgr.addMessage(snark, message);
+    }
+
+    public long getSavedTorrentTime(Snark snark) {
+        return _smgr.getSavedTorrentTime(snark);
+    }
+
+    public BitField getSavedTorrentBitField(Snark snark) {
+        return _smgr.getSavedTorrentBitField(snark);
+    }
+
+    //////// end CompleteListener methods
+
+    private void updateStatus(String s) {
+        _umgr.notifyProgress(this, s);
+    }
+}
-- 
GitLab