i2psnark: Add support for specifying data dir in add form (ticket #1028)

This commit is contained in:
zzz
2014-11-08 17:50:27 +00:00
parent 049044b827
commit 555189f123
4 changed files with 88 additions and 21 deletions

View File

@@ -1119,21 +1119,23 @@ public class SnarkManager implements CompleteListener {
* Caller must verify this torrent is not already added. * Caller must verify this torrent is not already added.
* *
* @param filename the absolute path to save the metainfo to, generally ending in ".torrent" * @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() * @throws RuntimeException via Snark.fatal()
*/ */
private void addTorrent(String filename) { private void addTorrent(String filename, File baseFile, boolean dontAutoStart) {
addTorrent(filename, null, false); addTorrent(filename, baseFile, dontAutoStart, null);
} }
/** /**
* Caller must verify this torrent is not already added. * Caller must verify this torrent is not already added.
* *
* @param filename the absolute path to save the metainfo to, generally ending in ".torrent" * @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() * @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()) { if ((!dontAutoStart) && !_util.connected()) {
addMessage(_("Connecting to I2P")); addMessage(_("Connecting to I2P"));
boolean ok = _util.connect(); boolean ok = _util.connect();
@@ -1150,7 +1152,8 @@ public class SnarkManager implements CompleteListener {
addMessage(_("Error: Could not add the torrent {0}", filename) + ": " + ioe); addMessage(_("Error: Could not add the torrent {0}", filename) + ": " + ioe);
return; return;
} }
File dataDir = getDataDir(); if (dataDir == null)
dataDir = getDataDir();
Snark torrent = null; Snark torrent = null;
synchronized (_snarks) { synchronized (_snarks) {
torrent = _snarks.get(filename); torrent = _snarks.get(filename);
@@ -1266,7 +1269,25 @@ public class SnarkManager implements CompleteListener {
*/ */
public void addMagnet(String name, byte[] ih, String trackerURL, boolean updateStatus) { public void addMagnet(String name, byte[] ih, String trackerURL, boolean updateStatus) {
// updateStatus is true from UI, false from config file bulk add // 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, * @param updateStatus should we add this magnet to the config file,
* to save it across restarts, in case we don't get * to save it across restarts, in case we don't get
* the metadata before shutdown? * 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 * @param listener to intercept callbacks, should pass through to this
* @return the new Snark or null on failure * @return the new Snark or null on failure
* @throws RuntimeException via Snark.fatal() * @throws RuntimeException via Snark.fatal()
* @since 0.9.4 * @since 0.9.4
*/ */
public Snark addMagnet(String name, byte[] ih, String trackerURL, boolean updateStatus, 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, Snark torrent = new Snark(_util, name, ih, trackerURL, listener,
_peerCoordinatorSet, _connectionAcceptor, _peerCoordinatorSet, _connectionAcceptor,
false, getDataDir().getPath()); false, dirPath);
synchronized (_snarks) { synchronized (_snarks) {
Snark snark = getTorrentByInfoHash(ih); 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 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 * @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. * Must be a filesystem-safe name.
* @param dataDir must exist, or null to default to snark data directory
* @throws RuntimeException via Snark.fatal() * @throws RuntimeException via Snark.fatal()
* @since 0.8.4 * @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 // prevent interference by DirMonitor
synchronized (_snarks) { synchronized (_snarks) {
boolean success = FileUtil.copy(fromfile.getAbsolutePath(), filename, false); boolean success = FileUtil.copy(fromfile.getAbsolutePath(), filename, false);
@@ -1422,7 +1446,7 @@ public class SnarkManager implements CompleteListener {
if (!areFilesPublic()) if (!areFilesPublic())
SecureFileOutputStream.setPerms(new File(filename)); SecureFileOutputStream.setPerms(new File(filename));
// hold the lock for a long time // hold the lock for a long time
addTorrent(filename); addTorrent(filename, null, false, dataDir);
} }
} }

View File

@@ -110,7 +110,7 @@ class UpdateRunner implements UpdateTask, CompleteListener {
_umgr.notifyAttemptFailed(this, "No tracker, no DHT, no OT", null); _umgr.notifyAttemptFailed(this, "No tracker, no DHT, no OT", null);
continue; continue;
} }
_snark = _smgr.addMagnet(name, ih, trackerURL, true, true, this); _snark = _smgr.addMagnet(name, ih, trackerURL, true, true, null, this);
if (_snark != null) { if (_snark != null) {
updateStatus("<b>" + _smgr.util().getString("Updating from {0}", linkify(updateURL)) + "</b>"); updateStatus("<b>" + _smgr.util().getString("Updating from {0}", linkify(updateURL)) + "</b>");
new Timeout(); new Timeout();

View File

@@ -50,6 +50,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
private final String _url; private final String _url;
private final byte[] _fakeHash; private final byte[] _fakeHash;
private final String _name; private final String _name;
private final File _dataDir;
private volatile long _remaining = -1; private volatile long _remaining = -1;
private volatile long _total = -1; private volatile long _total = -1;
private volatile long _transferred; private volatile long _transferred;
@@ -65,8 +66,10 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
/** /**
* Caller should call _mgr.addDownloader(this), which * Caller should call _mgr.addDownloader(this), which
* will start things off. * 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 // magnet constructor
super(mgr.util(), "Torrent download", super(mgr.util(), "Torrent download",
null, null, null, null, null, false, null); null, null, null, null, null, false, null);
@@ -75,6 +78,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
_mgr = mgr; _mgr = mgr;
_url = url; _url = url;
_name = _("Download torrent file from {0}", url); _name = _("Download torrent file from {0}", url);
_dataDir = dataDir;
byte[] fake = null; byte[] fake = null;
try { try {
fake = SHA1.getInstance().digest(url.getBytes("ISO-8859-1")); 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)); _mgr.addMessage(_("Torrent already in the queue: {0}", name));
} else { } else {
// This may take a LONG time to create the storage. // This may take a LONG time to create the storage.
_mgr.copyAndAddTorrent(file, canonical); _mgr.copyAndAddTorrent(file, canonical, _dataDir);
snark = _mgr.getTorrentByBaseName(originalName); snark = _mgr.getTorrentByBaseName(originalName);
if (snark != null) if (snark != null)
snark.startTorrent(); snark.startTorrent();

View File

@@ -30,6 +30,7 @@ import net.i2p.data.Base64;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.util.Log; import net.i2p.util.Log;
import net.i2p.util.SecureFile;
import org.klomp.snark.I2PSnarkUtil; import org.klomp.snark.I2PSnarkUtil;
import org.klomp.snark.MagnetURI; import org.klomp.snark.MagnetURI;
@@ -933,13 +934,41 @@ public class I2PSnarkServlet extends BasicServlet {
} else } else
*****/ *****/
if (newURL != null) { 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<Snark> 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://")) { if (newURL.startsWith("http://")) {
FetchAndAdd fetch = new FetchAndAdd(_context, _manager, newURL); FetchAndAdd fetch = new FetchAndAdd(_context, _manager, newURL, dir);
_manager.addDownloader(fetch); _manager.addDownloader(fetch);
} else if (newURL.startsWith(MagnetURI.MAGNET) || newURL.startsWith(MagnetURI.MAGGOT)) { } 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) { } 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 { } else {
_manager.addMessage(_("Invalid URL: Must start with \"http://\", \"{0}\", or \"{1}\"", _manager.addMessage(_("Invalid URL: Must start with \"http://\", \"{0}\", or \"{1}\"",
MagnetURI.MAGNET, MagnetURI.MAGGOT)); MagnetURI.MAGNET, MagnetURI.MAGGOT));
@@ -2000,6 +2029,7 @@ public class I2PSnarkServlet extends BasicServlet {
out.write(toThemeImg("add")); out.write(toThemeImg("add"));
out.write(' '); out.write(' ');
out.write(_("Add Torrent")); out.write(_("Add Torrent"));
out.write("</span><hr>\n<table border=\"0\"><tr><td>"); out.write("</span><hr>\n<table border=\"0\"><tr><td>");
out.write(_("From URL")); out.write(_("From URL"));
out.write(":<td><input type=\"text\" name=\"nofilter_newURL\" size=\"85\" value=\"" + newURL + "\" spellcheck=\"false\""); out.write(":<td><input type=\"text\" name=\"nofilter_newURL\" size=\"85\" value=\"" + newURL + "\" spellcheck=\"false\"");
@@ -2010,9 +2040,17 @@ public class I2PSnarkServlet extends BasicServlet {
//out.write("From file: <input type=\"file\" name=\"newFile\" size=\"50\" value=\"" + newFile + "\" /><br>"); //out.write("From file: <input type=\"file\" name=\"newFile\" size=\"50\" value=\"" + newFile + "\" /><br>");
out.write("<input type=\"submit\" class=\"add\" value=\""); out.write("<input type=\"submit\" class=\"add\" value=\"");
out.write(_("Add torrent")); out.write(_("Add torrent"));
out.write("\" name=\"foo\" ><br>\n"); out.write("\" name=\"foo\" ><br>\n" +
"<tr><td>");
out.write(_("Data dir"));
out.write(":<td><input type=\"text\" name=\"nofilter_newDir\" size=\"85\" value=\"\" spellcheck=\"false\"");
out.write(" title=\"");
out.write(_("Enter the directory to save the data in (default {0})", _manager.getDataDir().getAbsolutePath()));
out.write("\"></td></tr>\n");
out.write("<tr><td>&nbsp;<td><span class=\"snarkAddInfo\">"); out.write("<tr><td>&nbsp;<td><span class=\"snarkAddInfo\">");
out.write(_("You can also copy .torrent files to: {0}.", "<code>" + _manager.getDataDir().getAbsolutePath () + "</code>")); out.write(_("You can also copy .torrent files to: {0}.", "<code>" + _manager.getDataDir().getAbsolutePath() + "</code>"));
out.write("\n"); out.write("\n");
out.write(_("Removing a .torrent will cause it to stop.")); out.write(_("Removing a .torrent will cause it to stop."));
out.write("<br></span></table>\n"); out.write("<br></span></table>\n");
@@ -2372,15 +2410,16 @@ public class I2PSnarkServlet extends BasicServlet {
/** /**
* @param url in base32 or hex * @param url in base32 or hex
* @param dataDir null to default to snark data directory
* @since 0.8.4 * @since 0.8.4
*/ */
private void addMagnet(String url) { private void addMagnet(String url, File dataDir) {
try { try {
MagnetURI magnet = new MagnetURI(_manager.util(), url); MagnetURI magnet = new MagnetURI(_manager.util(), url);
String name = magnet.getName(); String name = magnet.getName();
byte[] ih = magnet.getInfoHash(); byte[] ih = magnet.getInfoHash();
String trackerURL = magnet.getTrackerURL(); String trackerURL = magnet.getTrackerURL();
_manager.addMagnet(name, ih, trackerURL, true); _manager.addMagnet(name, ih, trackerURL, true, dataDir);
} catch (IllegalArgumentException iae) { } catch (IllegalArgumentException iae) {
_manager.addMessage(_("Invalid magnet URL {0}", url)); _manager.addMessage(_("Invalid magnet URL {0}", url));
} }