diff --git a/apps/i2psnark/java/src/org/klomp/snark/CompleteListener.java b/apps/i2psnark/java/src/org/klomp/snark/CompleteListener.java index 5e2388aeb5539e2e71c7af36e4953fbc81571d84..77fa9c98b4395c459f4ccbcd626c5123ee941b66 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/CompleteListener.java +++ b/apps/i2psnark/java/src/org/klomp/snark/CompleteListener.java @@ -49,6 +49,11 @@ public interface CompleteListener { */ public void addMessage(Snark snark, String message); + /** + * @since 0.9.4 + */ + public void gotPiece(Snark snark); + // not really listeners but the easiest way to get back to an optional SnarkManager public long getSavedTorrentTime(Snark snark); public BitField getSavedTorrentBitField(Snark snark); diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java index 8ffde7363b9fbcf158d3009e00fbad4be62ec1fa..08c7109ef977dbf354367677d1fc27887d238f5a 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java @@ -515,8 +515,8 @@ class PeerCoordinator implements PeerListener peerCount = peers.size(); unchokePeer(); - if (listener != null) - listener.peerChange(this, peer); + //if (listener != null) + // listener.peerChange(this, peer); } } if (toDisconnect != null) { @@ -652,8 +652,8 @@ class PeerCoordinator implements PeerListener */ public boolean gotHave(Peer peer, int piece) { - if (listener != null) - listener.peerChange(this, peer); + //if (listener != null) + // listener.peerChange(this, peer); synchronized(wantedPieces) { for (Piece pc : wantedPieces) { @@ -672,8 +672,8 @@ class PeerCoordinator implements PeerListener */ public boolean gotBitField(Peer peer, BitField bitfield) { - if (listener != null) - listener.peerChange(this, peer); + //if (listener != null) + // listener.peerChange(this, peer); boolean rv = false; synchronized(wantedPieces) { @@ -919,8 +919,8 @@ class PeerCoordinator implements PeerListener { uploaded += size; - if (listener != null) - listener.peerChange(this, peer); + //if (listener != null) + // listener.peerChange(this, peer); } /** @@ -930,8 +930,8 @@ class PeerCoordinator implements PeerListener { downloaded += size; - if (listener != null) - listener.peerChange(this, peer); + //if (listener != null) + // listener.peerChange(this, peer); } /** @@ -1040,8 +1040,8 @@ class PeerCoordinator implements PeerListener if (_log.shouldLog(Log.INFO)) _log.info("Got choke(" + choke + "): " + peer); - if (listener != null) - listener.peerChange(this, peer); + //if (listener != null) + // listener.peerChange(this, peer); } public void gotInterest(Peer peer, boolean interest) @@ -1060,8 +1060,8 @@ class PeerCoordinator implements PeerListener } } - if (listener != null) - listener.peerChange(this, peer); + //if (listener != null) + // listener.peerChange(this, peer); } public void disconnected(Peer peer) @@ -1081,8 +1081,8 @@ class PeerCoordinator implements PeerListener peerCount = peers.size(); } - if (listener != null) - listener.peerChange(this, peer); + //if (listener != null) + // listener.peerChange(this, peer); } /** Called when a peer is removed, to prevent it from being used in diff --git a/apps/i2psnark/java/src/org/klomp/snark/Snark.java b/apps/i2psnark/java/src/org/klomp/snark/Snark.java index 531f3ddff4dbef973da629a786fd8a43ad06a22c..8a65bd51ed9bdfa2e067b1ff8d0cddf9056b2017 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Snark.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Snark.java @@ -1115,7 +1115,12 @@ public class Snark } } + + ///////////// Begin StorageListener methods + //private boolean allocating = false; + + /** does nothing */ public void storageCreateFile(Storage storage, String name, long length) { //if (allocating) @@ -1129,6 +1134,7 @@ public class Snark // How much storage space has been allocated private long allocated = 0; + /** does nothing */ public void storageAllocated(Storage storage, long length) { //allocating = true; @@ -1140,7 +1146,8 @@ public class Snark private boolean allChecked = false; private boolean checking = false; - private boolean prechecking = true; + //private boolean prechecking = true; + public void storageChecked(Storage storage, int num, boolean checked) { //allocating = false; @@ -1158,6 +1165,8 @@ public class Snark if (!checking) { if (_log.shouldLog(Log.INFO)) _log.info("Got " + (checked ? "" : "BAD ") + "piece: " + num); + if (completeListener != null) + completeListener.gotPiece(this); } } @@ -1187,6 +1196,9 @@ public class Snark coordinator.setWantedPieces(); } + ///////////// End StorageListener methods + + /** SnarkSnutdown callback unused */ public void shutdown() { diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index 4399b5f232f8cfdd40e94c79e28cb7c7e1e9ff8c..81d72ee84e0996fafd8d6babd8f31635575a34a8 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -1517,6 +1517,12 @@ public class SnarkManager implements CompleteListener { addMessage(message); } + /** + * A Snark.CompleteListener method. + * @since 0.9.4 + */ + public void gotPiece(Snark snark) {} + // End Snark.CompleteListeners /** diff --git a/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java b/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java index bb9906a30a22dda3a8ff6328db6458848d1d8b79..a676f33ab8059d7ee3b37f921e83aeda60f55ff9 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java +++ b/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java @@ -1,17 +1,15 @@ 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.SimpleTimer2; import net.i2p.util.VersionComparator; /** @@ -25,15 +23,16 @@ class UpdateRunner implements UpdateTask, CompleteListener { private final UpdateManager _umgr; private final SnarkManager _smgr; private final List<URI> _urls; - private final String _updateFile; private volatile boolean _isRunning; + private volatile boolean _hasMetaInfo; + private volatile boolean _isComplete; private final String _newVersion; - private ByteArrayOutputStream _baos; private URI _currentURI; private Snark _snark; - private boolean _hasMetaInfo; private static final long MAX_LENGTH = 30*1024*1024; + private static final long METAINFO_TIMEOUT = 30*60*1000; + private static final long COMPLETE_TIMEOUT = 3*60*60*1000; public UpdateRunner(I2PAppContext ctx, UpdateManager umgr, SnarkManager smgr, List<URI> uris, String newVersion) { @@ -43,7 +42,6 @@ class UpdateRunner implements UpdateTask, CompleteListener { _smgr = smgr; _urls = uris; _newVersion = newVersion; - _updateFile = (new File(ctx.getTempDir(), "update" + ctx.random().nextInt() + ".tmp")).getAbsolutePath(); } //////// begin UpdateTask methods @@ -79,7 +77,6 @@ class UpdateRunner implements UpdateTask, CompleteListener { * If it is, get the whole thing. */ private void update() { - if (_urls.isEmpty()) { _umgr.notifyTaskFailed(this, "", null); return; @@ -93,9 +90,16 @@ class UpdateRunner implements UpdateTask, CompleteListener { String name = magnet.getName(); byte[] ih = magnet.getInfoHash(); String trackerURL = magnet.getTrackerURL(); + if (trackerURL == null && !_smgr.util().shouldUseDHT() && + !_smgr.util().shouldUseOpenTrackers()) { + // but won't we use OT as a failsafe even if disabled? + _umgr.notifyAttemptFailed(this, "No tracker, no DHT, no OT", null); + continue; + } _snark = _smgr.addMagnet(name, ih, trackerURL, true, true, this); if (_snark != null) { updateStatus("<b>" + _smgr.util().getString("Updating from {0}", updateURL) + "</b>"); + new Timeout(); break; } } catch (IllegalArgumentException iae) {} @@ -104,6 +108,32 @@ class UpdateRunner implements UpdateTask, CompleteListener { fatal("No valid URLs"); } + /** + * This will run twice, once at the metainfo timeout and + * once at the complete timeout. + */ + private class Timeout extends SimpleTimer2.TimedEvent { + private final long _start = _context.clock().now(); + + public Timeout() { + super(_context.simpleTimer2(), METAINFO_TIMEOUT); + } + + public void timeReached() { + if (_isComplete || !_isRunning) + return; + if (!_hasMetaInfo) { + fatal("Metainfo timeout"); + return; + } + if (_context.clock().now() - _start >= COMPLETE_TIMEOUT) { + fatal("Complete timeout"); + return; + } + reschedule(COMPLETE_TIMEOUT - METAINFO_TIMEOUT); + } + } + private void fatal(String error) { if (_snark != null) { if (_hasMetaInfo) { @@ -138,15 +168,21 @@ class UpdateRunner implements UpdateTask, CompleteListener { } _umgr.notifyComplete(this, _newVersion, f); _smgr.torrentComplete(snark); + _isComplete = true; } + /** + * This is called by stopTorrent() among others + */ public void updateStatus(Snark snark) { - + if (snark.isStopped()) { + if (!_isComplete) + fatal("stopped by user"); + } _smgr.updateStatus(snark); } public String gotMetaInfo(Snark snark) { - Storage storage = snark.getStorage(); MetaInfo info = snark.getMetaInfo(); if (info.getFiles() != null) { fatal("more than 1 file"); @@ -173,6 +209,16 @@ class UpdateRunner implements UpdateTask, CompleteListener { _smgr.addMessage(snark, message); } + public void gotPiece(Snark snark) { + if (_hasMetaInfo) { + long total = snark.getTotalLength(); + long remaining = snark.getRemainingLength(); + String status = "<b>" + _smgr.util().getString("Updating") + "</b>"; + _umgr.notifyProgress(this, status, total - remaining, total); + } + _smgr.gotPiece(snark); + } + public long getSavedTorrentTime(Snark snark) { return _smgr.getSavedTorrentTime(snark); }