i2psnark:

- Support arbitrary location for torrent data. Save location in
   per-torrent config file. TODO: Fix torrent browse pages
   (ticket #1028)
 - Enhance idle shutdown message
 - Javadocs
This commit is contained in:
zzz
2014-01-27 13:41:38 +00:00
parent 18146daad8
commit 47712a39ac
6 changed files with 138 additions and 47 deletions

View File

@ -66,7 +66,8 @@ class IdleChecker extends SimpleTimer2.TimedEvent {
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN))
_log.warn("Closing tunnels on idle"); _log.warn("Closing tunnels on idle");
_util.disconnect(); _util.disconnect();
_mgr.addMessage(_util.getString("I2P tunnel closed.")); _mgr.addMessage(_util.getString("No more torrents running.") + ' ' +
_util.getString("I2P tunnel closed."));
schedule(3 * CHECK_TIME); schedule(3 * CHECK_TIME);
return; return;
} }

View File

@ -163,7 +163,7 @@ class PeerState implements DataLoader
_log.debug(peer + " rcv bitfield"); _log.debug(peer + " rcv bitfield");
if (bitfield != null) if (bitfield != null)
{ {
// XXX - Be liberal in what you except? // XXX - Be liberal in what you accept?
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN))
_log.warn("Got unexpected bitfield message from " + peer); _log.warn("Got unexpected bitfield message from " + peer);
return; return;

View File

@ -34,6 +34,7 @@ import net.i2p.I2PAppContext;
import net.i2p.client.streaming.I2PServerSocket; import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import net.i2p.util.Log; import net.i2p.util.Log;
import net.i2p.util.SecureFile;
/** /**
* Main Snark program startup class. * Main Snark program startup class.
@ -238,13 +239,21 @@ public class Snark
private volatile boolean _autoStoppable; private volatile boolean _autoStoppable;
/** from main() via parseArguments() single torrent */ /**
* from main() via parseArguments() single torrent
*
* @deprecated unused
*/
Snark(I2PSnarkUtil util, String torrent, String ip, int user_port, Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
StorageListener slistener, CoordinatorListener clistener) { StorageListener slistener, CoordinatorListener clistener) {
this(util, torrent, ip, user_port, slistener, clistener, null, null, null, true, "."); this(util, torrent, ip, user_port, slistener, clistener, null, null, null, true, ".");
} }
/** single torrent - via router */ /**
* single torrent - via router
*
* @deprecated unused
*/
public Snark(I2PAppContext ctx, Properties opts, String torrent, public Snark(I2PAppContext ctx, Properties opts, String torrent,
StorageListener slistener, boolean start, String rootDir) { StorageListener slistener, boolean start, String rootDir) {
this(new I2PSnarkUtil(ctx), torrent, null, -1, slistener, null, null, null, null, false, rootDir); this(new I2PSnarkUtil(ctx), torrent, null, -1, slistener, null, null, null, null, false, rootDir);
@ -275,11 +284,28 @@ public class Snark
this.startTorrent(); this.startTorrent();
} }
/** multitorrent */ /**
* multitorrent
*/
public Snark(I2PSnarkUtil util, String torrent, String ip, int user_port, public Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
StorageListener slistener, CoordinatorListener clistener, StorageListener slistener, CoordinatorListener clistener,
CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet, CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet,
ConnectionAcceptor connectionAcceptor, boolean start, String rootDir) ConnectionAcceptor connectionAcceptor, boolean start, String rootDir)
{
this(util, torrent, ip, user_port, slistener, clistener, complistener,
peerCoordinatorSet, connectionAcceptor, start, rootDir, null);
}
/**
* multitorrent
*
* @param baseFile if null, use rootDir/torrentName; if non-null, use it instead
* @since 0.9.11
*/
public Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
StorageListener slistener, CoordinatorListener clistener,
CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet,
ConnectionAcceptor connectionAcceptor, boolean start, String rootDir, File baseFile)
{ {
if (slistener == null) if (slistener == null)
slistener = this; slistener = this;
@ -395,7 +421,14 @@ public class Snark
try try
{ {
activity = "Checking storage"; activity = "Checking storage";
storage = new Storage(_util, rootDataDir, meta, slistener); if (baseFile == null) {
String base = Storage.filterName(meta.getName());
if (_util.getFilesPublic())
baseFile = new File(rootDataDir, base);
else
baseFile = new SecureFile(rootDataDir, base);
}
storage = new Storage(_util, baseFile, meta, slistener);
if (completeListener != null) { if (completeListener != null) {
storage.check(completeListener.getSavedTorrentTime(this), storage.check(completeListener.getSavedTorrentTime(this),
completeListener.getSavedTorrentBitField(this)); completeListener.getSavedTorrentBitField(this));
@ -1102,8 +1135,14 @@ public class Snark
*/ */
public void gotMetaInfo(PeerCoordinator coordinator, MetaInfo metainfo) { public void gotMetaInfo(PeerCoordinator coordinator, MetaInfo metainfo) {
try { try {
String base = Storage.filterName(metainfo.getName());
File baseFile;
if (_util.getFilesPublic())
baseFile = new File(rootDataDir, base);
else
baseFile = new SecureFile(rootDataDir, base);
// The following two may throw IOE... // The following two may throw IOE...
storage = new Storage(_util, rootDataDir, metainfo, this); storage = new Storage(_util, baseFile, metainfo, this);
storage.check(); storage.check();
// ... so don't set meta until here // ... so don't set meta until here
meta = metainfo; meta = metainfo;

View File

@ -86,6 +86,7 @@ public class SnarkManager implements CompleteListener {
public static final String PROP_DIR = "i2psnark.dir"; public static final String PROP_DIR = "i2psnark.dir";
private static final String PROP_META_PREFIX = "i2psnark.zmeta."; private static final String PROP_META_PREFIX = "i2psnark.zmeta.";
private static final String PROP_META_STAMP = "stamp"; private static final String PROP_META_STAMP = "stamp";
private static final String PROP_META_BASE = "base";
private static final String PROP_META_BITFIELD = "bitfield"; private static final String PROP_META_BITFIELD = "bitfield";
private static final String PROP_META_PRIORITY = "priority"; private static final String PROP_META_PRIORITY = "priority";
private static final String PROP_META_BITFIELD_SUFFIX = ".bitfield"; private static final String PROP_META_BITFIELD_SUFFIX = ".bitfield";
@ -347,7 +348,7 @@ public class SnarkManager implements CompleteListener {
* *
* @return the new config directory, non-null * @return the new config directory, non-null
* @throws RuntimeException on creation fail * @throws RuntimeException on creation fail
* @since 0.9.10 * @since 0.9.11
*/ */
private File migrateConfig(File oldFile) { private File migrateConfig(File oldFile) {
File dir = new SecureDirectory(oldFile + CONFIG_DIR_SUFFIX); File dir = new SecureDirectory(oldFile + CONFIG_DIR_SUFFIX);
@ -455,7 +456,7 @@ public class SnarkManager implements CompleteListener {
/** /**
* The config for a torrent * The config for a torrent
* @return non-null, possibly empty * @return non-null, possibly empty
* @since 0.9.10 * @since 0.9.11
*/ */
private Properties getConfig(Snark snark) { private Properties getConfig(Snark snark) {
return getConfig(snark.getInfoHash()); return getConfig(snark.getInfoHash());
@ -465,7 +466,7 @@ public class SnarkManager implements CompleteListener {
* The config for a torrent * The config for a torrent
* @param ih 20-byte infohash * @param ih 20-byte infohash
* @return non-null, possibly empty * @return non-null, possibly empty
* @since 0.9.10 * @since 0.9.11
*/ */
private Properties getConfig(byte[] ih) { private Properties getConfig(byte[] ih) {
Properties rv = new OrderedProperties(); Properties rv = new OrderedProperties();
@ -482,7 +483,7 @@ public class SnarkManager implements CompleteListener {
* The config file for a torrent * The config file for a torrent
* @param confDir the config directory * @param confDir the config directory
* @param ih 20-byte infohash * @param ih 20-byte infohash
* @since 0.9.10 * @since 0.9.11
*/ */
private static File configFile(File confDir, byte[] ih) { private static File configFile(File confDir, byte[] ih) {
String hex = I2PSnarkUtil.toHex(ih); String hex = I2PSnarkUtil.toHex(ih);
@ -1071,15 +1072,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 baseFile may be null, if so look in rootDataDir
* @throws RuntimeException via Snark.fatal() * @throws RuntimeException via Snark.fatal()
*/ */
private void addTorrent(String filename) { addTorrent(filename, false); } private void addTorrent(String filename) {
addTorrent(filename, null, false);
}
/** /**
* 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 baseFile may be null, if so look in rootDataDir
* @throws RuntimeException via Snark.fatal() * @throws RuntimeException via Snark.fatal()
*/ */
private void addTorrent(String filename, boolean dontAutoStart) { private void addTorrent(String filename, File baseFile, boolean dontAutoStart) {
if ((!dontAutoStart) && !_util.connected()) { if ((!dontAutoStart) && !_util.connected()) {
addMessage(_("Connecting to I2P")); addMessage(_("Connecting to I2P"));
boolean ok = _util.connect(); boolean ok = _util.connect();
@ -1160,9 +1169,13 @@ public class SnarkManager implements CompleteListener {
} else { } else {
// TODO load saved closest DHT nodes and pass to the Snark ? // TODO load saved closest DHT nodes and pass to the Snark ?
// This may take a LONG time // This may take a LONG time
if (baseFile == null)
baseFile = getSavedBaseFile(info.getInfoHash());
if (_log.shouldLog(Log.INFO))
_log.info("New Snark, torrent: " + filename + " base: " + baseFile);
torrent = new Snark(_util, filename, null, -1, null, null, this, torrent = new Snark(_util, filename, null, -1, null, null, this,
_peerCoordinatorSet, _connectionAcceptor, _peerCoordinatorSet, _connectionAcceptor,
false, dataDir.getPath()); false, dataDir.getPath(), baseFile);
loadSavedFilePriorities(torrent); loadSavedFilePriorities(torrent);
synchronized (_snarks) { synchronized (_snarks) {
_snarks.put(filename, torrent); _snarks.put(filename, torrent);
@ -1305,14 +1318,17 @@ public class SnarkManager implements CompleteListener {
* This verifies that a torrent with this infohash is not already added. * This verifies that a torrent with this infohash is not already added.
* This may take a LONG time to create or check the storage. * This may take a LONG time to create or check the storage.
* *
* Called from servlet.
*
* @param metainfo the metainfo for the torrent * @param metainfo the metainfo for the torrent
* @param bitfield the current completion status of the torrent * @param bitfield the current completion status 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 * @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 baseFile may be null, if so look in rootDataDir
* @throws RuntimeException via Snark.fatal() * @throws RuntimeException via Snark.fatal()
* @since 0.8.4 * @since 0.8.4
*/ */
public void addTorrent(MetaInfo metainfo, BitField bitfield, String filename, boolean dontAutoStart) throws IOException { public void addTorrent(MetaInfo metainfo, BitField bitfield, String filename, File baseFile, boolean dontAutoStart) throws IOException {
// prevent interference by DirMonitor // prevent interference by DirMonitor
synchronized (_snarks) { synchronized (_snarks) {
Snark snark = getTorrentByInfoHash(metainfo.getInfoHash()); Snark snark = getTorrentByInfoHash(metainfo.getInfoHash());
@ -1321,11 +1337,11 @@ public class SnarkManager implements CompleteListener {
return; return;
} }
// so addTorrent won't recheck // so addTorrent won't recheck
saveTorrentStatus(metainfo, bitfield, null); // no file priorities saveTorrentStatus(metainfo, bitfield, null, baseFile); // no file priorities
try { try {
locked_writeMetaInfo(metainfo, filename, areFilesPublic()); locked_writeMetaInfo(metainfo, filename, areFilesPublic());
// hold the lock for a long time // hold the lock for a long time
addTorrent(filename, dontAutoStart); addTorrent(filename, baseFile, dontAutoStart);
} catch (IOException ioe) { } catch (IOException ioe) {
addMessage(_("Failed to copy torrent file to {0}", filename)); addMessage(_("Failed to copy torrent file to {0}", filename));
_log.error("Failed to write torrent file", ioe); _log.error("Failed to write torrent file", ioe);
@ -1461,6 +1477,19 @@ public class SnarkManager implements CompleteListener {
} }
storage.setFilePriorities(rv); storage.setFilePriorities(rv);
} }
/**
* Get the base location for a torrent from the config file.
* @return File or null, doesn't necessarily exist
* @since 0.9.11
*/
public File getSavedBaseFile(byte[] ih) {
Properties config = getConfig(ih);
String base = config.getProperty(PROP_META_BASE);
if (base == null)
return null;
return new File(base);
}
/** /**
* Save the completion status of a torrent and the current time in the config file * Save the completion status of a torrent and the current time in the config file
@ -1471,14 +1500,15 @@ public class SnarkManager implements CompleteListener {
* *
* @param bitfield non-null * @param bitfield non-null
* @param priorities may be null * @param priorities may be null
* @param base may be null
*/ */
public void saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities) { public void saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities, File base) {
synchronized (_configLock) { synchronized (_configLock) {
locked_saveTorrentStatus(metainfo, bitfield, priorities); locked_saveTorrentStatus(metainfo, bitfield, priorities, base);
} }
} }
private void locked_saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities) { private void locked_saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities, File base) {
byte[] ih = metainfo.getInfoHash(); byte[] ih = metainfo.getInfoHash();
String bfs; String bfs;
if (bitfield.complete()) { if (bitfield.complete()) {
@ -1490,6 +1520,8 @@ public class SnarkManager implements CompleteListener {
Properties config = getConfig(ih); Properties config = getConfig(ih);
config.setProperty(PROP_META_STAMP, Long.toString(System.currentTimeMillis())); config.setProperty(PROP_META_STAMP, Long.toString(System.currentTimeMillis()));
config.setProperty(PROP_META_BITFIELD, bfs); config.setProperty(PROP_META_BITFIELD, bfs);
if (base != null)
config.setProperty(PROP_META_BASE, base.getAbsolutePath());
// now the file priorities // now the file priorities
if (priorities != null) { if (priorities != null) {
@ -1742,7 +1774,7 @@ public class SnarkManager implements CompleteListener {
MetaInfo meta = snark.getMetaInfo(); MetaInfo meta = snark.getMetaInfo();
Storage storage = snark.getStorage(); Storage storage = snark.getStorage();
if (meta != null && storage != null) if (meta != null && storage != null)
saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities()); saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities(), storage.getBase());
} }
/** /**
@ -1764,7 +1796,7 @@ public class SnarkManager implements CompleteListener {
snark.stopTorrent(); snark.stopTorrent();
return null; return null;
} }
saveTorrentStatus(meta, storage.getBitField(), null); // no file priorities saveTorrentStatus(meta, storage.getBitField(), null, storage.getBase()); // no file priorities
// temp for addMessage() in case canonical throws // temp for addMessage() in case canonical throws
String name = storage.getBaseName(); String name = storage.getBaseName();
try { try {
@ -1865,7 +1897,7 @@ public class SnarkManager implements CompleteListener {
try { try {
// Snark.fatal() throws a RuntimeException // Snark.fatal() throws a RuntimeException
// don't let one bad torrent kill the whole loop // don't let one bad torrent kill the whole loop
addTorrent(name, !shouldAutoStart()); addTorrent(name, null, !shouldAutoStart());
} catch (Exception e) { } catch (Exception e) {
addMessage(_("Error: Could not add the torrent {0}", name) + ": " + e); addMessage(_("Error: Could not add the torrent {0}", name) + ": " + e);
_log.error("Unable to add the torrent " + name, e); _log.error("Unable to add the torrent " + name, e);

View File

@ -86,20 +86,18 @@ public class Storage
private static final ByteCache _cache = ByteCache.getInstance(16, BUFSIZE); private static final ByteCache _cache = ByteCache.getInstance(16, BUFSIZE);
/** /**
* Creates a new storage based on the supplied MetaInfo. This will * Creates a new storage based on the supplied MetaInfo.
*
* Does not check storage. Caller MUST call check(), which will
* try to create and/or check all needed files in the MetaInfo. * try to create and/or check all needed files in the MetaInfo.
* *
* Does not check storage. Caller MUST call check() * @param baseFile the torrent data file or dir
*/ */
public Storage(I2PSnarkUtil util, File rootDir, MetaInfo metainfo, StorageListener listener) public Storage(I2PSnarkUtil util, File baseFile, MetaInfo metainfo, StorageListener listener)
{ {
_util = util; _util = util;
_log = util.getContext().logManager().getLog(Storage.class); _log = util.getContext().logManager().getLog(Storage.class);
boolean areFilesPublic = _util.getFilesPublic(); _base = baseFile;
if (areFilesPublic)
_base = new File(rootDir, filterName(metainfo.getName()));
else
_base = new SecureFile(rootDir, filterName(metainfo.getName()));
this.metainfo = metainfo; this.metainfo = metainfo;
this.listener = listener; this.listener = listener;
needed = metainfo.getPieces(); needed = metainfo.getPieces();
@ -708,7 +706,8 @@ public class Storage
/** /**
* The base file or directory. * The base file or directory.
* @return a new List * @return the File
* @since 0.9.11
*/ */
public File getBase() { public File getBase() {
return _base; return _base;
@ -716,8 +715,8 @@ public class Storage
/** /**
* Does not include directories. Unsorted. * Does not include directories. Unsorted.
* @since 0.9.10
* @return a new List * @return a new List
* @since 0.9.11
*/ */
public List<File> getFiles() { public List<File> getFiles() {
List<File> rv = new ArrayList<File>(_torrentFiles.size()); List<File> rv = new ArrayList<File>(_torrentFiles.size());
@ -731,7 +730,7 @@ public class Storage
* Includes the base for a multi-file torrent. * Includes the base for a multi-file torrent.
* Sorted bottom-up for easy deletion. * Sorted bottom-up for easy deletion.
* Slow. Use for deletion only. * Slow. Use for deletion only.
* @since 0.9.10 * @since 0.9.11
* @return a new Set or null for a single-file torrent * @return a new Set or null for a single-file torrent
*/ */
public SortedSet<File> getDirectories() { public SortedSet<File> getDirectories() {

View File

@ -896,7 +896,9 @@ public class I2PSnarkServlet extends BasicServlet {
} else if ("Create".equals(action)) { } else if ("Create".equals(action)) {
String baseData = req.getParameter("baseFile"); String baseData = req.getParameter("baseFile");
if (baseData != null && baseData.trim().length() > 0) { if (baseData != null && baseData.trim().length() > 0) {
File baseFile = new File(_manager.getDataDir(), baseData); File baseFile = new File(baseData.trim());
if (!baseFile.isAbsolute())
baseFile = new File(_manager.getDataDir(), baseData);
String announceURL = req.getParameter("announceURL"); String announceURL = req.getParameter("announceURL");
// make the user add a tracker on the config form now // make the user add a tracker on the config form now
//String announceURLOther = req.getParameter("announceURLOther"); //String announceURLOther = req.getParameter("announceURLOther");
@ -956,7 +958,7 @@ public class I2PSnarkServlet extends BasicServlet {
File torrentFile = new File(_manager.getDataDir(), s.getBaseName() + ".torrent"); File torrentFile = new File(_manager.getDataDir(), s.getBaseName() + ".torrent");
// FIXME is the storage going to stay around thanks to the info reference? // FIXME is the storage going to stay around thanks to the info reference?
// now add it, but don't automatically start it // now add it, but don't automatically start it
_manager.addTorrent(info, s.getBitField(), torrentFile.getAbsolutePath(), true); _manager.addTorrent(info, s.getBitField(), torrentFile.getAbsolutePath(), baseFile, true);
_manager.addMessage(_("Torrent created for \"{0}\"", baseFile.getName()) + ": " + torrentFile.getAbsolutePath()); _manager.addMessage(_("Torrent created for \"{0}\"", baseFile.getName()) + ": " + torrentFile.getAbsolutePath());
if (announceURL != null && !_manager.util().getOpenTrackers().contains(announceURL)) if (announceURL != null && !_manager.util().getOpenTrackers().contains(announceURL))
_manager.addMessage(_("Many I2P trackers require you to register new torrents before seeding - please do so before starting \"{0}\"", baseFile.getName())); _manager.addMessage(_("Many I2P trackers require you to register new torrents before seeding - please do so before starting \"{0}\"", baseFile.getName()));
@ -1708,10 +1710,11 @@ public class I2PSnarkServlet extends BasicServlet {
out.write("</span><hr>\n<table border=\"0\"><tr><td>"); out.write("</span><hr>\n<table border=\"0\"><tr><td>");
//out.write("From file: <input type=\"file\" name=\"newFile\" size=\"50\" value=\"" + newFile + "\" /><br>\n"); //out.write("From file: <input type=\"file\" name=\"newFile\" size=\"50\" value=\"" + newFile + "\" /><br>\n");
out.write(_("Data to seed")); out.write(_("Data to seed"));
out.write(":<td><code>" + _manager.getDataDir().getAbsolutePath() + File.separatorChar out.write(":<td>"
+ "</code><input type=\"text\" name=\"baseFile\" size=\"58\" value=\"" + baseFile + "<input type=\"text\" name=\"baseFile\" size=\"58\" value=\"" + baseFile
+ "\" spellcheck=\"false\" title=\""); + "\" spellcheck=\"false\" title=\"");
out.write(_("File or directory to seed (must be within the specified path)")); out.write(_("File or directory to seed (full path or within the directory {0} )",
_manager.getDataDir().getAbsolutePath() + File.separatorChar));
out.write("\" ><tr><td>\n"); out.write("\" ><tr><td>\n");
out.write(_("Trackers")); out.write(_("Trackers"));
out.write(":<td><table style=\"width: 30%;\"><tr><td></td><td align=\"center\">"); out.write(":<td><table style=\"width: 30%;\"><tr><td></td><td align=\"center\">");
@ -2198,12 +2201,6 @@ public class I2PSnarkServlet extends BasicServlet {
private String getListHTML(File r, String base, boolean parent, Map<String, String[]> postParams) private String getListHTML(File r, String base, boolean parent, Map<String, String[]> postParams)
throws IOException throws IOException
{ {
File[] ls = null;
if (r.isDirectory()) {
ls = r.listFiles();
Arrays.sort(ls, new ListingComparator());
} // if r is not a directory, we are only showing torrent info section
String title = decodePath(base); String title = decodePath(base);
String cpath = _contextPath + '/'; String cpath = _contextPath + '/';
if (title.startsWith(cpath)) if (title.startsWith(cpath))
@ -2249,7 +2246,8 @@ public class I2PSnarkServlet extends BasicServlet {
if (parent) // always true if (parent) // always true
buf.append("<div class=\"page\"><div class=\"mainsection\">"); buf.append("<div class=\"page\"><div class=\"mainsection\">");
boolean showPriority = ls != null && snark != null && snark.getStorage() != null && !snark.getStorage().complete(); boolean showPriority = snark != null && snark.getStorage() != null && !snark.getStorage().complete() &&
r.isDirectory();
if (showPriority) { if (showPriority) {
buf.append("<form action=\"").append(base).append("\" method=\"POST\">\n"); buf.append("<form action=\"").append(base).append("\" method=\"POST\">\n");
buf.append("<input type=\"hidden\" name=\"nonce\" value=\"").append(_nonce).append("\" >\n"); buf.append("<input type=\"hidden\" name=\"nonce\" value=\"").append(_nonce).append("\" >\n");
@ -2271,6 +2269,12 @@ public class I2PSnarkServlet extends BasicServlet {
.append(":</b> <a href=\"").append(_contextPath).append('/').append(baseName).append("\">") .append(":</b> <a href=\"").append(_contextPath).append('/').append(baseName).append("\">")
.append(fullPath) .append(fullPath)
.append("</a></td></tr>\n"); .append("</a></td></tr>\n");
buf.append("<tr><td>")
.append("<img alt=\"\" border=\"0\" src=\"").append(_imgPath).append("file.png\" >&nbsp;<b>")
.append(_("Data location"))
.append(":</b> ")
.append(urlEncode(snark.getStorage().getBase().getPath()))
.append("</td></tr>\n");
MetaInfo meta = snark.getMetaInfo(); MetaInfo meta = snark.getMetaInfo();
if (meta != null) { if (meta != null) {
@ -2404,6 +2408,22 @@ public class I2PSnarkServlet extends BasicServlet {
.append("\"</th></tr>\n"); .append("\"</th></tr>\n");
} }
buf.append("</table>\n"); buf.append("</table>\n");
if (snark != null && !r.exists()) {
// fixup TODO
buf.append("<p>Does not exist<br>resource=\"").append(r.toString())
.append("\"<br>base=\"").append(base)
.append("\"<br>torrent=\"").append(torrentName)
.append("\"</p></div></div></BODY></HTML>");
return buf.toString();
}
File[] ls = null;
if (r.isDirectory()) {
ls = r.listFiles();
Arrays.sort(ls, new ListingComparator());
} // if r is not a directory, we are only showing torrent info section
if (ls == null) { if (ls == null) {
// We are only showing the torrent info section // We are only showing the torrent info section
buf.append("</div></div></BODY></HTML>"); buf.append("</div></div></BODY></HTML>");
@ -2655,6 +2675,6 @@ public class I2PSnarkServlet extends BasicServlet {
} }
} }
snark.updatePiecePriorities(); snark.updatePiecePriorities();
_manager.saveTorrentStatus(snark.getMetaInfo(), storage.getBitField(), storage.getFilePriorities()); _manager.saveTorrentStatus(snark.getMetaInfo(), storage.getBitField(), storage.getFilePriorities(), storage.getBase());
} }
} }