forked from I2P_Developers/i2p.i2p
i2psnark:
- Fix announce hosts of the form b64dest[.i2p] - Add last activity stat - Disallow illegal filenames on Windows - cleanups and log tweaks
This commit is contained in:
@ -656,10 +656,10 @@ public class Snark
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
savedUploaded = nowUploaded;
|
||||
if (changed && completeListener != null)
|
||||
completeListener.updateStatus(this);
|
||||
// TODO should save comments at shutdown even if never started...
|
||||
// SnarkManager.stopAllTorrents() will save comments at shutdown even if never started...
|
||||
if (completeListener != null) {
|
||||
if (changed)
|
||||
completeListener.updateStatus(this);
|
||||
synchronized(_commentLock) {
|
||||
if (_comments != null) {
|
||||
synchronized(_comments) {
|
||||
|
@ -116,6 +116,8 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
private static final String PROP_META_MAGNET_PREFIX = "i2psnark.magnet.";
|
||||
/** @since 0.9.31 */
|
||||
private static final String PROP_META_COMMENTS = "comments";
|
||||
/** @since 0.9.42 */
|
||||
private static final String PROP_META_ACTIVITY = "activity";
|
||||
|
||||
private static final String CONFIG_FILE_SUFFIX = ".config";
|
||||
private static final String CONFIG_FILE = "i2psnark" + CONFIG_FILE_SUFFIX;
|
||||
@ -1609,13 +1611,16 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
}
|
||||
// ok, snark created, now lets start it up or configure it further
|
||||
Properties config = getConfig(torrent);
|
||||
boolean running;
|
||||
String prop = config.getProperty(PROP_META_RUNNING);
|
||||
if(prop == null || Boolean.parseBoolean(prop)) {
|
||||
running = true;
|
||||
} else {
|
||||
running = false;
|
||||
String prop = config.getProperty(PROP_META_RUNNING);
|
||||
boolean running = prop == null || Boolean.parseBoolean(prop);
|
||||
prop = config.getProperty(PROP_META_ACTIVITY);
|
||||
if (prop != null && torrent.getStorage() != null) {
|
||||
try {
|
||||
long activity = Long.parseLong(prop);
|
||||
torrent.getStorage().setActivity(activity);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
|
||||
// Were we running last time?
|
||||
String link = linkify(torrent);
|
||||
if (!dontAutoStart && shouldAutoStart() && running) {
|
||||
@ -1779,7 +1784,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
addMessage(_t("Torrent with this info hash is already running: {0}", snark.getBaseName()));
|
||||
return false;
|
||||
} else if (bitfield != null) {
|
||||
saveTorrentStatus(metainfo, bitfield, null, false, baseFile, true, 0, true); // no file priorities
|
||||
saveTorrentStatus(metainfo, bitfield, null, false, baseFile, true, 0, 0, true); // no file priorities
|
||||
}
|
||||
// so addTorrent won't recheck
|
||||
if (filename == null) {
|
||||
@ -2041,7 +2046,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
return;
|
||||
saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities(), storage.getInOrder(),
|
||||
storage.getBase(), storage.getPreserveFileNames(),
|
||||
snark.getUploaded(), snark.isStopped(), comments);
|
||||
snark.getUploaded(), storage.getActivity(), snark.isStopped(), comments);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2057,8 +2062,8 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
* @param base may be null
|
||||
*/
|
||||
private void saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities, boolean inOrder,
|
||||
File base, boolean preserveNames, long uploaded, boolean stopped) {
|
||||
saveTorrentStatus(metainfo, bitfield, priorities, inOrder, base, preserveNames, uploaded, stopped, null);
|
||||
File base, boolean preserveNames, long uploaded, long activity, boolean stopped) {
|
||||
saveTorrentStatus(metainfo, bitfield, priorities, inOrder, base, preserveNames, uploaded, activity, stopped, null);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2066,15 +2071,15 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
* @since 0.9.31
|
||||
*/
|
||||
private void saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities, boolean inOrder,
|
||||
File base, boolean preserveNames, long uploaded, boolean stopped,
|
||||
File base, boolean preserveNames, long uploaded, long activity, boolean stopped,
|
||||
Boolean comments) {
|
||||
synchronized (_configLock) {
|
||||
locked_saveTorrentStatus(metainfo, bitfield, priorities, inOrder, base, preserveNames, uploaded, stopped, comments);
|
||||
locked_saveTorrentStatus(metainfo, bitfield, priorities, inOrder, base, preserveNames, uploaded, activity, stopped, comments);
|
||||
}
|
||||
}
|
||||
|
||||
private void locked_saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities, boolean inOrder,
|
||||
File base, boolean preserveNames, long uploaded, boolean stopped,
|
||||
File base, boolean preserveNames, long uploaded, long activity, boolean stopped,
|
||||
Boolean comments) {
|
||||
byte[] ih = metainfo.getInfoHash();
|
||||
Properties config = getConfig(ih);
|
||||
@ -2104,6 +2109,8 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
config.setProperty(PROP_META_BASE, base.getAbsolutePath());
|
||||
if (comments != null)
|
||||
config.setProperty(PROP_META_COMMENTS, comments.toString());
|
||||
if (activity > 0)
|
||||
config.setProperty(PROP_META_ACTIVITY, Long.toString(activity));
|
||||
|
||||
// now the file priorities
|
||||
if (priorities != null) {
|
||||
@ -2469,7 +2476,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
Storage storage = snark.getStorage();
|
||||
if (meta != null && storage != null)
|
||||
saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities(), storage.getInOrder(),
|
||||
storage.getBase(), storage.getPreserveFileNames(), snark.getUploaded(),
|
||||
storage.getBase(), storage.getPreserveFileNames(), snark.getUploaded(), storage.getActivity(),
|
||||
snark.isStopped());
|
||||
}
|
||||
|
||||
@ -2493,7 +2500,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
return null;
|
||||
}
|
||||
saveTorrentStatus(meta, storage.getBitField(), null, false,
|
||||
storage.getBase(), storage.getPreserveFileNames(), 0,
|
||||
storage.getBase(), storage.getPreserveFileNames(), 0, 0,
|
||||
snark.isStopped());
|
||||
// temp for addMessage() in case canonical throws
|
||||
String name = storage.getBaseName();
|
||||
|
@ -35,12 +35,14 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import gnu.getopt.Getopt;
|
||||
|
||||
@ -78,6 +80,7 @@ public class Storage implements Closeable
|
||||
private boolean _inOrder;
|
||||
private final AtomicInteger _allocateCount = new AtomicInteger();
|
||||
private final AtomicInteger _checkProgress = new AtomicInteger();
|
||||
private final AtomicLong _activity = new AtomicLong();
|
||||
|
||||
/** The default piece size. */
|
||||
private static final int DEFAULT_PIECE_SIZE = 256*1024;
|
||||
@ -319,6 +322,28 @@ public class Storage implements Closeable
|
||||
changed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.42
|
||||
*/
|
||||
public long getActivity() {
|
||||
return _activity.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.42
|
||||
*/
|
||||
private void setActivity() {
|
||||
setActivity(I2PAppContext.getGlobalContext().clock().now());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.42
|
||||
*/
|
||||
public void setActivity(long time) {
|
||||
_activity.set(time);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* File checking in progress.
|
||||
* @since 0.9.3
|
||||
@ -827,6 +852,13 @@ public class Storage implements Closeable
|
||||
0x2028, 0x2029
|
||||
};
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file
|
||||
private static final String[] WIN_ILLEGAL = new String[] {
|
||||
"con", "prn", "aux", "nul",
|
||||
"com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9",
|
||||
"lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9"
|
||||
};
|
||||
|
||||
/**
|
||||
* Filter the name, but only if configured to do so.
|
||||
* We will do so on torrents received from others, but not
|
||||
@ -859,8 +891,18 @@ public class Storage implements Closeable
|
||||
rv = "_";
|
||||
} else {
|
||||
rv = name;
|
||||
if (rv.startsWith("."))
|
||||
if (rv.startsWith(".")) {
|
||||
rv = '_' + rv.substring(1);
|
||||
} else if (SystemVersion.isWindows()) {
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file
|
||||
String iname = name.toLowerCase(Locale.US);
|
||||
for (int i = 0; i < WIN_ILLEGAL.length; i++) {
|
||||
String w = WIN_ILLEGAL[i];
|
||||
if (iname.equals(w) ||
|
||||
(iname.startsWith(w + '.') && w.indexOf('.', w.length() + 1) < 0))
|
||||
rv = '_' + rv;
|
||||
}
|
||||
}
|
||||
if (rv.endsWith(".") || rv.endsWith(" "))
|
||||
rv = rv.substring(0, rv.length() - 1) + '_';
|
||||
for (int i = 0; i < ILLEGAL.length; i++) {
|
||||
@ -1227,6 +1269,7 @@ public class Storage implements Closeable
|
||||
}
|
||||
bs = rv.getData();
|
||||
getUncheckedPiece(piece, bs, off, len);
|
||||
setActivity();
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -1311,7 +1354,7 @@ public class Storage implements Closeable
|
||||
pp.release();
|
||||
}
|
||||
|
||||
changed = true;
|
||||
setActivity();
|
||||
|
||||
// do this after the write, so we know it succeeded, and we don't set the
|
||||
// needed count to zero, which would cause checkRAF() to open the file readonly.
|
||||
@ -1567,8 +1610,7 @@ public class Storage implements Closeable
|
||||
* Caller must synchronize and call checkRAF() or openRAF().
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public synchronized void balloonFile() throws IOException
|
||||
{
|
||||
private synchronized void balloonFile() throws IOException {
|
||||
long remaining = length;
|
||||
final int ZEROBLOCKSIZE = (int) Math.min(remaining, 32*1024);
|
||||
byte[] zeros = new byte[ZEROBLOCKSIZE];
|
||||
|
@ -292,7 +292,7 @@ public class TrackerClient implements Runnable {
|
||||
_log.debug("Announce: [" + primary + "] infoHash: " + infoHash);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Skipping invalid or non-i2p announce: " + primary);
|
||||
_log.warn("Skipping invalid or non-i2p announce: " + primary + " for torrent " + snark.getBaseName());
|
||||
}
|
||||
} else {
|
||||
_log.warn("No primary announce");
|
||||
@ -366,7 +366,7 @@ public class TrackerClient implements Runnable {
|
||||
Hash h = getHostHash(ann);
|
||||
if (h == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Bad announce URL: [" + ann + ']');
|
||||
_log.warn("Bad announce URL: [" + ann + "] for torrent " + snark.getBaseName());
|
||||
return false;
|
||||
}
|
||||
// comment this out if tracker.welterde.i2p upgrades
|
||||
@ -374,19 +374,19 @@ public class TrackerClient implements Runnable {
|
||||
Destination dest = _util.getMyDestination();
|
||||
if (dest != null && dest.getSigType() != SigType.DSA_SHA1) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Skipping incompatible tracker: " + ann);
|
||||
_log.warn("Skipping incompatible tracker: " + ann + " for torrent " + snark.getBaseName());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (existing.size() >= MAX_TRACKERS) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Not using announce URL, we have enough: [" + ann + ']');
|
||||
_log.info("Not using announce URL, we have enough: [" + ann + "] for torrent " + snark.getBaseName());
|
||||
return false;
|
||||
}
|
||||
boolean rv = existing.add(h);
|
||||
if (!rv) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Dup announce URL: [" + ann + ']');
|
||||
_log.info("Dup announce URL: [" + ann + "] for torrent " + snark.getBaseName());
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
@ -605,7 +605,7 @@ public class TrackerClient implements Runnable {
|
||||
tplc.startsWith(ERROR_GOT_HTML) || // fake msg from doRequest()
|
||||
(!tr.isPrimary && tr.registerFails > MAX_REGISTER_FAILS / 2))
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Not longer announcing to " + tr.announce + " : " +
|
||||
_log.warn("No longer announcing to " + tr.announce + " : " +
|
||||
tr.trackerProblems + " after " + tr.registerFails + " failures");
|
||||
tr.stop = true;
|
||||
//
|
||||
@ -917,8 +917,21 @@ public class TrackerClient implements Runnable {
|
||||
if (!"http".equals(url.getScheme()))
|
||||
return null;
|
||||
String host = url.getHost();
|
||||
if (host == null)
|
||||
if (host == null) {
|
||||
// URI can't handle b64dest or b64dest.i2p if it contains '~'
|
||||
// but it doesn't throw an exception, just returns a null host
|
||||
if (ann.startsWith("http://") && ann.length() >= 7 + 516 && ann.contains("~")) {
|
||||
ann = ann.substring(7);
|
||||
int slash = ann.indexOf('/');
|
||||
if (slash >= 516) {
|
||||
ann = ann.substring(0, slash);
|
||||
if (ann.endsWith(".i2p"))
|
||||
ann = ann.substring(0, ann.length() - 4);
|
||||
return ConvertToHash.getHash(ann);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (host.endsWith(".i2p")) {
|
||||
String path = url.getPath();
|
||||
if (path == null || !path.startsWith("/"))
|
||||
|
@ -3106,7 +3106,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
long dat = meta.getCreationDate();
|
||||
// needs locale configured for automatic translation
|
||||
SimpleDateFormat fmt = new SimpleDateFormat("HH:mm, EEEE dd MMMM yyyy");
|
||||
SimpleDateFormat fmt = new SimpleDateFormat("EEEE dd MMMM yyyy HH:mm");
|
||||
fmt.setTimeZone(SystemVersion.getSystemTimeZone(_context));
|
||||
if (dat > 0) {
|
||||
String date = fmt.format(new Date(dat));
|
||||
@ -3147,6 +3147,18 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
.append(date)
|
||||
.append("</td></tr>\n");
|
||||
}
|
||||
if (storage != null) {
|
||||
dat = storage.getActivity();
|
||||
if (dat > 0) {
|
||||
String date = fmt.format(new Date(dat));
|
||||
buf.append("<tr><td>");
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Last activity")).append(":</b> ")
|
||||
.append(date)
|
||||
.append("</td></tr>\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (meta == null || !meta.isPrivate()) {
|
||||
|
Reference in New Issue
Block a user