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