From 555189f1238717851c29ad190390b4c964534a17 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 8 Nov 2014 17:50:27 +0000 Subject: [PATCH] i2psnark: Add support for specifying data dir in add form (ticket #1028) --- .../src/org/klomp/snark/SnarkManager.java | 46 ++++++++++++---- .../src/org/klomp/snark/UpdateRunner.java | 2 +- .../src/org/klomp/snark/web/FetchAndAdd.java | 8 ++- .../org/klomp/snark/web/I2PSnarkServlet.java | 53 ++++++++++++++++--- 4 files changed, 88 insertions(+), 21 deletions(-) diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index 8344469bc..51952d39d 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -1119,21 +1119,23 @@ public class SnarkManager implements CompleteListener { * Caller must verify this torrent is not already added. * * @param filename the absolute path to save the metainfo to, generally ending in ".torrent" - * @param baseFile may be null, if so look in rootDataDir + * @param baseFile may be null, if so look in dataDir * @throws RuntimeException via Snark.fatal() */ - private void addTorrent(String filename) { - addTorrent(filename, null, false); + private void addTorrent(String filename, File baseFile, boolean dontAutoStart) { + addTorrent(filename, baseFile, dontAutoStart, null); } /** * Caller must verify this torrent is not already added. * * @param filename the absolute path to save the metainfo to, generally ending in ".torrent" - * @param baseFile may be null, if so look in rootDataDir + * @param baseFile may be null, if so look in dataDir + * @param dataDir must exist, or null to default to snark data directory * @throws RuntimeException via Snark.fatal() + * @since 0.9.17 */ - private void addTorrent(String filename, File baseFile, boolean dontAutoStart) { + private void addTorrent(String filename, File baseFile, boolean dontAutoStart, File dataDir) { if ((!dontAutoStart) && !_util.connected()) { addMessage(_("Connecting to I2P")); boolean ok = _util.connect(); @@ -1150,7 +1152,8 @@ public class SnarkManager implements CompleteListener { addMessage(_("Error: Could not add the torrent {0}", filename) + ": " + ioe); return; } - File dataDir = getDataDir(); + if (dataDir == null) + dataDir = getDataDir(); Snark torrent = null; synchronized (_snarks) { torrent = _snarks.get(filename); @@ -1266,7 +1269,25 @@ public class SnarkManager implements CompleteListener { */ public void addMagnet(String name, byte[] ih, String trackerURL, boolean updateStatus) { // updateStatus is true from UI, false from config file bulk add - addMagnet(name, ih, trackerURL, updateStatus, updateStatus, this); + addMagnet(name, ih, trackerURL, updateStatus, updateStatus, null, this); + } + + /** + * Add a torrent with the info hash alone (magnet / maggot) + * + * @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 dataDir must exist, or null to default to snark data directory + * @throws RuntimeException via Snark.fatal() + * @since 0.9.17 + */ + public void addMagnet(String name, byte[] ih, String trackerURL, boolean updateStatus, File dataDir) { + // updateStatus is true from UI, false from config file bulk add + addMagnet(name, ih, trackerURL, updateStatus, updateStatus, dataDir, this); } /** @@ -1279,16 +1300,18 @@ public class SnarkManager implements CompleteListener { * @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 dataDir must exist, or null to default to snark data directory * @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) { + boolean autoStart, File dataDir, CompleteListener listener) { + String dirPath = dataDir != null ? dataDir.getAbsolutePath() : getDataDir().getPath(); Snark torrent = new Snark(_util, name, ih, trackerURL, listener, _peerCoordinatorSet, _connectionAcceptor, - false, getDataDir().getPath()); + false, dirPath); synchronized (_snarks) { Snark snark = getTorrentByInfoHash(ih); @@ -1407,10 +1430,11 @@ public class SnarkManager implements CompleteListener { * @param fromfile where the file is now, presumably in a temp directory somewhere * @param filename the absolute path to save the metainfo to, generally ending in ".torrent", which is also the name of the torrent * Must be a filesystem-safe name. + * @param dataDir must exist, or null to default to snark data directory * @throws RuntimeException via Snark.fatal() * @since 0.8.4 */ - public void copyAndAddTorrent(File fromfile, String filename) throws IOException { + public void copyAndAddTorrent(File fromfile, String filename, File dataDir) throws IOException { // prevent interference by DirMonitor synchronized (_snarks) { boolean success = FileUtil.copy(fromfile.getAbsolutePath(), filename, false); @@ -1422,7 +1446,7 @@ public class SnarkManager implements CompleteListener { if (!areFilesPublic()) SecureFileOutputStream.setPerms(new File(filename)); // hold the lock for a long time - addTorrent(filename); + addTorrent(filename, null, false, dataDir); } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java b/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java index d7f3b6690..4f9dff55a 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java +++ b/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java @@ -110,7 +110,7 @@ class UpdateRunner implements UpdateTask, CompleteListener { _umgr.notifyAttemptFailed(this, "No tracker, no DHT, no OT", null); continue; } - _snark = _smgr.addMagnet(name, ih, trackerURL, true, true, this); + _snark = _smgr.addMagnet(name, ih, trackerURL, true, true, null, this); if (_snark != null) { updateStatus("" + _smgr.util().getString("Updating from {0}", linkify(updateURL)) + ""); new Timeout(); diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/FetchAndAdd.java b/apps/i2psnark/java/src/org/klomp/snark/web/FetchAndAdd.java index d24d4f87c..99e2f62a6 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/FetchAndAdd.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/FetchAndAdd.java @@ -50,6 +50,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl private final String _url; private final byte[] _fakeHash; private final String _name; + private final File _dataDir; private volatile long _remaining = -1; private volatile long _total = -1; private volatile long _transferred; @@ -65,8 +66,10 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl /** * Caller should call _mgr.addDownloader(this), which * will start things off. + * + * @param dataDir null to default to snark data directory */ - public FetchAndAdd(I2PAppContext ctx, SnarkManager mgr, String url) { + public FetchAndAdd(I2PAppContext ctx, SnarkManager mgr, String url, File dataDir) { // magnet constructor super(mgr.util(), "Torrent download", null, null, null, null, null, false, null); @@ -75,6 +78,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl _mgr = mgr; _url = url; _name = _("Download torrent file from {0}", url); + _dataDir = dataDir; byte[] fake = null; try { fake = SHA1.getInstance().digest(url.getBytes("ISO-8859-1")); @@ -176,7 +180,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl _mgr.addMessage(_("Torrent already in the queue: {0}", name)); } else { // This may take a LONG time to create the storage. - _mgr.copyAndAddTorrent(file, canonical); + _mgr.copyAndAddTorrent(file, canonical, _dataDir); snark = _mgr.getTorrentByBaseName(originalName); if (snark != null) snark.startTorrent(); 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 4276679e7..a085864c1 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -30,6 +30,7 @@ import net.i2p.data.Base64; import net.i2p.data.DataHelper; import net.i2p.data.Hash; import net.i2p.util.Log; +import net.i2p.util.SecureFile; import org.klomp.snark.I2PSnarkUtil; import org.klomp.snark.MagnetURI; @@ -933,13 +934,41 @@ public class I2PSnarkServlet extends BasicServlet { } else *****/ if (newURL != null) { + String newDir = req.getParameter("nofilter_newDir"); + File dir = null; + if (newDir != null) { + newDir = newDir.trim(); + if (newDir.length() > 0) { + dir = new SecureFile(newDir); + if (!dir.isAbsolute()) { + _manager.addMessage(_("Data directory must be an absolute path") + ": " + dir); + return; + } + if (!dir.isDirectory() && !dir.mkdirs()) { + _manager.addMessage(_("Data directory cannot be created") + ": " + dir); + return; + } + Collection snarks = _manager.getTorrents(); + for (Snark s : snarks) { + Storage storage = s.getStorage(); + if (storage == null) + continue; + File sbase = storage.getBase(); + if (isParentOf(sbase, dir)) { + _manager.addMessage(_("Cannot add torrent {0} inside another torrent: {1}", + dir.getAbsolutePath(), sbase)); + return; + } + } + } + } if (newURL.startsWith("http://")) { - FetchAndAdd fetch = new FetchAndAdd(_context, _manager, newURL); + FetchAndAdd fetch = new FetchAndAdd(_context, _manager, newURL, dir); _manager.addDownloader(fetch); } else if (newURL.startsWith(MagnetURI.MAGNET) || newURL.startsWith(MagnetURI.MAGGOT)) { - addMagnet(newURL); + addMagnet(newURL, dir); } else if (newURL.length() == 40 && newURL.replaceAll("[a-fA-F0-9]", "").length() == 0) { - addMagnet(MagnetURI.MAGNET_FULL + newURL); + addMagnet(MagnetURI.MAGNET_FULL + newURL, dir); } else { _manager.addMessage(_("Invalid URL: Must start with \"http://\", \"{0}\", or \"{1}\"", MagnetURI.MAGNET, MagnetURI.MAGGOT)); @@ -2000,6 +2029,7 @@ public class I2PSnarkServlet extends BasicServlet { out.write(toThemeImg("add")); out.write(' '); out.write(_("Add Torrent")); + out.write("
\n\n"); + out.write("
"); out.write(_("From URL")); out.write(":
"); out.write("
\n"); + out.write("\" name=\"foo\" >
\n" + + + "
"); + out.write(_("Data dir")); + out.write(":
 "); - out.write(_("You can also copy .torrent files to: {0}.", "" + _manager.getDataDir().getAbsolutePath () + "")); + out.write(_("You can also copy .torrent files to: {0}.", "" + _manager.getDataDir().getAbsolutePath() + "")); out.write("\n"); out.write(_("Removing a .torrent will cause it to stop.")); out.write("
\n"); @@ -2372,15 +2410,16 @@ public class I2PSnarkServlet extends BasicServlet { /** * @param url in base32 or hex + * @param dataDir null to default to snark data directory * @since 0.8.4 */ - private void addMagnet(String url) { + private void addMagnet(String url, File dataDir) { try { MagnetURI magnet = new MagnetURI(_manager.util(), url); String name = magnet.getName(); byte[] ih = magnet.getInfoHash(); String trackerURL = magnet.getTrackerURL(); - _manager.addMagnet(name, ih, trackerURL, true); + _manager.addMagnet(name, ih, trackerURL, true, dataDir); } catch (IllegalArgumentException iae) { _manager.addMessage(_("Invalid magnet URL {0}", url)); }