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();
|
ioe.printStackTrace();
|
||||||
}
|
}
|
||||||
savedUploaded = nowUploaded;
|
savedUploaded = nowUploaded;
|
||||||
if (changed && completeListener != null)
|
// SnarkManager.stopAllTorrents() will save comments at shutdown even if never started...
|
||||||
completeListener.updateStatus(this);
|
|
||||||
// TODO should save comments at shutdown even if never started...
|
|
||||||
if (completeListener != null) {
|
if (completeListener != null) {
|
||||||
|
if (changed)
|
||||||
|
completeListener.updateStatus(this);
|
||||||
synchronized(_commentLock) {
|
synchronized(_commentLock) {
|
||||||
if (_comments != null) {
|
if (_comments != null) {
|
||||||
synchronized(_comments) {
|
synchronized(_comments) {
|
||||||
|
@ -116,6 +116,8 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
|||||||
private static final String PROP_META_MAGNET_PREFIX = "i2psnark.magnet.";
|
private static final String PROP_META_MAGNET_PREFIX = "i2psnark.magnet.";
|
||||||
/** @since 0.9.31 */
|
/** @since 0.9.31 */
|
||||||
private static final String PROP_META_COMMENTS = "comments";
|
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_SUFFIX = ".config";
|
||||||
private static final String CONFIG_FILE = "i2psnark" + CONFIG_FILE_SUFFIX;
|
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
|
// ok, snark created, now lets start it up or configure it further
|
||||||
Properties config = getConfig(torrent);
|
Properties config = getConfig(torrent);
|
||||||
boolean running;
|
String prop = config.getProperty(PROP_META_RUNNING);
|
||||||
String prop = config.getProperty(PROP_META_RUNNING);
|
boolean running = prop == null || Boolean.parseBoolean(prop);
|
||||||
if(prop == null || Boolean.parseBoolean(prop)) {
|
prop = config.getProperty(PROP_META_ACTIVITY);
|
||||||
running = true;
|
if (prop != null && torrent.getStorage() != null) {
|
||||||
} else {
|
try {
|
||||||
running = false;
|
long activity = Long.parseLong(prop);
|
||||||
|
torrent.getStorage().setActivity(activity);
|
||||||
|
} catch (NumberFormatException nfe) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Were we running last time?
|
// Were we running last time?
|
||||||
String link = linkify(torrent);
|
String link = linkify(torrent);
|
||||||
if (!dontAutoStart && shouldAutoStart() && running) {
|
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()));
|
addMessage(_t("Torrent with this info hash is already running: {0}", snark.getBaseName()));
|
||||||
return false;
|
return false;
|
||||||
} else if (bitfield != null) {
|
} 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
|
// so addTorrent won't recheck
|
||||||
if (filename == null) {
|
if (filename == null) {
|
||||||
@ -2041,7 +2046,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
|||||||
return;
|
return;
|
||||||
saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities(), storage.getInOrder(),
|
saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities(), storage.getInOrder(),
|
||||||
storage.getBase(), storage.getPreserveFileNames(),
|
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
|
* @param base may be null
|
||||||
*/
|
*/
|
||||||
private void saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities, boolean inOrder,
|
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) {
|
||||||
saveTorrentStatus(metainfo, bitfield, priorities, inOrder, base, preserveNames, uploaded, stopped, null);
|
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
|
* @since 0.9.31
|
||||||
*/
|
*/
|
||||||
private void saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities, boolean inOrder,
|
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) {
|
Boolean comments) {
|
||||||
synchronized (_configLock) {
|
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,
|
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) {
|
Boolean comments) {
|
||||||
byte[] ih = metainfo.getInfoHash();
|
byte[] ih = metainfo.getInfoHash();
|
||||||
Properties config = getConfig(ih);
|
Properties config = getConfig(ih);
|
||||||
@ -2104,6 +2109,8 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
|||||||
config.setProperty(PROP_META_BASE, base.getAbsolutePath());
|
config.setProperty(PROP_META_BASE, base.getAbsolutePath());
|
||||||
if (comments != null)
|
if (comments != null)
|
||||||
config.setProperty(PROP_META_COMMENTS, comments.toString());
|
config.setProperty(PROP_META_COMMENTS, comments.toString());
|
||||||
|
if (activity > 0)
|
||||||
|
config.setProperty(PROP_META_ACTIVITY, Long.toString(activity));
|
||||||
|
|
||||||
// now the file priorities
|
// now the file priorities
|
||||||
if (priorities != null) {
|
if (priorities != null) {
|
||||||
@ -2469,7 +2476,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
|||||||
Storage storage = snark.getStorage();
|
Storage storage = snark.getStorage();
|
||||||
if (meta != null && storage != null)
|
if (meta != null && storage != null)
|
||||||
saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities(), storage.getInOrder(),
|
saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities(), storage.getInOrder(),
|
||||||
storage.getBase(), storage.getPreserveFileNames(), snark.getUploaded(),
|
storage.getBase(), storage.getPreserveFileNames(), snark.getUploaded(), storage.getActivity(),
|
||||||
snark.isStopped());
|
snark.isStopped());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2493,7 +2500,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
saveTorrentStatus(meta, storage.getBitField(), null, false,
|
saveTorrentStatus(meta, storage.getBitField(), null, false,
|
||||||
storage.getBase(), storage.getPreserveFileNames(), 0,
|
storage.getBase(), storage.getPreserveFileNames(), 0, 0,
|
||||||
snark.isStopped());
|
snark.isStopped());
|
||||||
// temp for addMessage() in case canonical throws
|
// temp for addMessage() in case canonical throws
|
||||||
String name = storage.getBaseName();
|
String name = storage.getBaseName();
|
||||||
|
@ -35,12 +35,14 @@ import java.util.Collections;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import gnu.getopt.Getopt;
|
import gnu.getopt.Getopt;
|
||||||
|
|
||||||
@ -78,6 +80,7 @@ public class Storage implements Closeable
|
|||||||
private boolean _inOrder;
|
private boolean _inOrder;
|
||||||
private final AtomicInteger _allocateCount = new AtomicInteger();
|
private final AtomicInteger _allocateCount = new AtomicInteger();
|
||||||
private final AtomicInteger _checkProgress = new AtomicInteger();
|
private final AtomicInteger _checkProgress = new AtomicInteger();
|
||||||
|
private final AtomicLong _activity = new AtomicLong();
|
||||||
|
|
||||||
/** The default piece size. */
|
/** The default piece size. */
|
||||||
private static final int DEFAULT_PIECE_SIZE = 256*1024;
|
private static final int DEFAULT_PIECE_SIZE = 256*1024;
|
||||||
@ -319,6 +322,28 @@ public class Storage implements Closeable
|
|||||||
changed = false;
|
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.
|
* File checking in progress.
|
||||||
* @since 0.9.3
|
* @since 0.9.3
|
||||||
@ -827,6 +852,13 @@ public class Storage implements Closeable
|
|||||||
0x2028, 0x2029
|
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.
|
* Filter the name, but only if configured to do so.
|
||||||
* We will do so on torrents received from others, but not
|
* We will do so on torrents received from others, but not
|
||||||
@ -859,8 +891,18 @@ public class Storage implements Closeable
|
|||||||
rv = "_";
|
rv = "_";
|
||||||
} else {
|
} else {
|
||||||
rv = name;
|
rv = name;
|
||||||
if (rv.startsWith("."))
|
if (rv.startsWith(".")) {
|
||||||
rv = '_' + rv.substring(1);
|
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(" "))
|
if (rv.endsWith(".") || rv.endsWith(" "))
|
||||||
rv = rv.substring(0, rv.length() - 1) + '_';
|
rv = rv.substring(0, rv.length() - 1) + '_';
|
||||||
for (int i = 0; i < ILLEGAL.length; i++) {
|
for (int i = 0; i < ILLEGAL.length; i++) {
|
||||||
@ -1227,6 +1269,7 @@ public class Storage implements Closeable
|
|||||||
}
|
}
|
||||||
bs = rv.getData();
|
bs = rv.getData();
|
||||||
getUncheckedPiece(piece, bs, off, len);
|
getUncheckedPiece(piece, bs, off, len);
|
||||||
|
setActivity();
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1311,7 +1354,7 @@ public class Storage implements Closeable
|
|||||||
pp.release();
|
pp.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
changed = true;
|
setActivity();
|
||||||
|
|
||||||
// do this after the write, so we know it succeeded, and we don't set the
|
// 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.
|
// 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().
|
* Caller must synchronize and call checkRAF() or openRAF().
|
||||||
* @since 0.9.1
|
* @since 0.9.1
|
||||||
*/
|
*/
|
||||||
public synchronized void balloonFile() throws IOException
|
private synchronized void balloonFile() throws IOException {
|
||||||
{
|
|
||||||
long remaining = length;
|
long remaining = length;
|
||||||
final int ZEROBLOCKSIZE = (int) Math.min(remaining, 32*1024);
|
final int ZEROBLOCKSIZE = (int) Math.min(remaining, 32*1024);
|
||||||
byte[] zeros = new byte[ZEROBLOCKSIZE];
|
byte[] zeros = new byte[ZEROBLOCKSIZE];
|
||||||
|
@ -292,7 +292,7 @@ public class TrackerClient implements Runnable {
|
|||||||
_log.debug("Announce: [" + primary + "] infoHash: " + infoHash);
|
_log.debug("Announce: [" + primary + "] infoHash: " + infoHash);
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldLog(Log.WARN))
|
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 {
|
} else {
|
||||||
_log.warn("No primary announce");
|
_log.warn("No primary announce");
|
||||||
@ -366,7 +366,7 @@ public class TrackerClient implements Runnable {
|
|||||||
Hash h = getHostHash(ann);
|
Hash h = getHostHash(ann);
|
||||||
if (h == null) {
|
if (h == null) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Bad announce URL: [" + ann + ']');
|
_log.warn("Bad announce URL: [" + ann + "] for torrent " + snark.getBaseName());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// comment this out if tracker.welterde.i2p upgrades
|
// comment this out if tracker.welterde.i2p upgrades
|
||||||
@ -374,19 +374,19 @@ public class TrackerClient implements Runnable {
|
|||||||
Destination dest = _util.getMyDestination();
|
Destination dest = _util.getMyDestination();
|
||||||
if (dest != null && dest.getSigType() != SigType.DSA_SHA1) {
|
if (dest != null && dest.getSigType() != SigType.DSA_SHA1) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Skipping incompatible tracker: " + ann);
|
_log.warn("Skipping incompatible tracker: " + ann + " for torrent " + snark.getBaseName());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (existing.size() >= MAX_TRACKERS) {
|
if (existing.size() >= MAX_TRACKERS) {
|
||||||
if (_log.shouldLog(Log.INFO))
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
boolean rv = existing.add(h);
|
boolean rv = existing.add(h);
|
||||||
if (!rv) {
|
if (!rv) {
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info("Dup announce URL: [" + ann + ']');
|
_log.info("Dup announce URL: [" + ann + "] for torrent " + snark.getBaseName());
|
||||||
}
|
}
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
@ -605,7 +605,7 @@ public class TrackerClient implements Runnable {
|
|||||||
tplc.startsWith(ERROR_GOT_HTML) || // fake msg from doRequest()
|
tplc.startsWith(ERROR_GOT_HTML) || // fake msg from doRequest()
|
||||||
(!tr.isPrimary && tr.registerFails > MAX_REGISTER_FAILS / 2))
|
(!tr.isPrimary && tr.registerFails > MAX_REGISTER_FAILS / 2))
|
||||||
if (_log.shouldLog(Log.WARN))
|
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.trackerProblems + " after " + tr.registerFails + " failures");
|
||||||
tr.stop = true;
|
tr.stop = true;
|
||||||
//
|
//
|
||||||
@ -917,8 +917,21 @@ public class TrackerClient implements Runnable {
|
|||||||
if (!"http".equals(url.getScheme()))
|
if (!"http".equals(url.getScheme()))
|
||||||
return null;
|
return null;
|
||||||
String host = url.getHost();
|
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;
|
return null;
|
||||||
|
}
|
||||||
if (host.endsWith(".i2p")) {
|
if (host.endsWith(".i2p")) {
|
||||||
String path = url.getPath();
|
String path = url.getPath();
|
||||||
if (path == null || !path.startsWith("/"))
|
if (path == null || !path.startsWith("/"))
|
||||||
|
@ -3106,7 +3106,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
|||||||
}
|
}
|
||||||
long dat = meta.getCreationDate();
|
long dat = meta.getCreationDate();
|
||||||
// needs locale configured for automatic translation
|
// 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));
|
fmt.setTimeZone(SystemVersion.getSystemTimeZone(_context));
|
||||||
if (dat > 0) {
|
if (dat > 0) {
|
||||||
String date = fmt.format(new Date(dat));
|
String date = fmt.format(new Date(dat));
|
||||||
@ -3147,6 +3147,18 @@ public class I2PSnarkServlet extends BasicServlet {
|
|||||||
.append(date)
|
.append(date)
|
||||||
.append("</td></tr>\n");
|
.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()) {
|
if (meta == null || !meta.isPrivate()) {
|
||||||
|
@ -15,6 +15,7 @@ import net.i2p.I2PException;
|
|||||||
import net.i2p.client.I2PSession;
|
import net.i2p.client.I2PSession;
|
||||||
import net.i2p.client.I2PSessionException;
|
import net.i2p.client.I2PSessionException;
|
||||||
import net.i2p.data.Base32;
|
import net.i2p.data.Base32;
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
@ -39,7 +40,8 @@ import net.i2p.util.SocketTimeout;
|
|||||||
* Supports http://example.i2p/blah
|
* Supports http://example.i2p/blah
|
||||||
* Supports http://B32KEY.b32.i2p/blah
|
* Supports http://B32KEY.b32.i2p/blah
|
||||||
* Supports http://i2p/B64KEY/blah for compatibility with the eepproxy
|
* Supports http://i2p/B64KEY/blah for compatibility with the eepproxy
|
||||||
* Supports http://B64KEY/blah for compatibility with the eepproxy
|
* Supports http://B64KEY/blah for as of 0.9.42
|
||||||
|
* Supports http://B64KEY.i2p/blah as of 0.9.42
|
||||||
* Warning - does not support /eepproxy/blah, address helpers, http://B64KEY.i2p/blah,
|
* Warning - does not support /eepproxy/blah, address helpers, http://B64KEY.i2p/blah,
|
||||||
* or other odd things that may be found in the HTTP proxy.
|
* or other odd things that may be found in the HTTP proxy.
|
||||||
*
|
*
|
||||||
@ -115,9 +117,28 @@ public class I2PSocketEepGet extends EepGet {
|
|||||||
try {
|
try {
|
||||||
URI url = new URI(_actualURL);
|
URI url = new URI(_actualURL);
|
||||||
if ("http".equals(url.getScheme())) {
|
if ("http".equals(url.getScheme())) {
|
||||||
|
Destination dest = null;
|
||||||
String host = url.getHost();
|
String host = url.getHost();
|
||||||
if (host == null)
|
if (host == null) {
|
||||||
throw new MalformedURLException("no hostname: " + _actualURL);
|
String ann = _actualURL;
|
||||||
|
// URI can't handle 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);
|
||||||
|
try {
|
||||||
|
dest = new Destination(ann);
|
||||||
|
} catch (DataFormatException dfe) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dest == null)
|
||||||
|
throw new MalformedURLException("no hostname: " + _actualURL);
|
||||||
|
// won't pick up the port either, but the path will be OK
|
||||||
|
}
|
||||||
int port = url.getPort();
|
int port = url.getPort();
|
||||||
if (port <= 0 || port > 65535)
|
if (port <= 0 || port > 65535)
|
||||||
port = 80;
|
port = 80;
|
||||||
@ -139,31 +160,32 @@ public class I2PSocketEepGet extends EepGet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use existing I2PSession for lookups.
|
if (dest == null) {
|
||||||
// This is much more efficient than using the naming service
|
// Use existing I2PSession for lookups.
|
||||||
Destination dest;
|
// This is much more efficient than using the naming service
|
||||||
I2PSession sess = _socketManager.getSession();
|
I2PSession sess = _socketManager.getSession();
|
||||||
if (sess != null && !sess.isClosed()) {
|
if (sess != null && !sess.isClosed()) {
|
||||||
try {
|
try {
|
||||||
if (host.length() == 60 && host.endsWith(".b32.i2p")) {
|
if (host.length() == 60 && host.endsWith(".b32.i2p")) {
|
||||||
byte[] b = Base32.decode(host.substring(0, 52));
|
byte[] b = Base32.decode(host.substring(0, 52));
|
||||||
if (b != null) {
|
if (b != null) {
|
||||||
Hash h = Hash.create(b);
|
Hash h = Hash.create(b);
|
||||||
dest = sess.lookupDest(h, 20*1000);
|
dest = sess.lookupDest(h, 20*1000);
|
||||||
|
} else {
|
||||||
|
dest = null;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
dest = null;
|
dest = sess.lookupDest(host, 20*1000);
|
||||||
}
|
}
|
||||||
} else {
|
} catch (I2PSessionException ise) {
|
||||||
dest = sess.lookupDest(host, 20*1000);
|
dest = null;
|
||||||
}
|
}
|
||||||
} catch (I2PSessionException ise) {
|
} else {
|
||||||
dest = null;
|
dest = _context.namingService().lookup(host);
|
||||||
}
|
}
|
||||||
} else {
|
if (dest == null)
|
||||||
dest = _context.namingService().lookup(host);
|
throw new UnknownHostException("Unknown or non-i2p host: " + host);
|
||||||
}
|
}
|
||||||
if (dest == null)
|
|
||||||
throw new UnknownHostException("Unknown or non-i2p host: " + host);
|
|
||||||
|
|
||||||
// Set the timeouts, using the other existing options in the socket manager
|
// Set the timeouts, using the other existing options in the socket manager
|
||||||
// This currently duplicates what SocketTimeout is doing in EepGet,
|
// This currently duplicates what SocketTimeout is doing in EepGet,
|
||||||
@ -257,7 +279,7 @@ public class I2PSocketEepGet extends EepGet {
|
|||||||
* Uses I2CP at localhost:7654 with a single 1-hop tunnel each direction.
|
* Uses I2CP at localhost:7654 with a single 1-hop tunnel each direction.
|
||||||
* Tunnel build time not included in the timeout.
|
* Tunnel build time not included in the timeout.
|
||||||
*
|
*
|
||||||
* This is just for testing, it will be commented out someday.
|
* This is just for testing.
|
||||||
* Real command line apps should use EepGet.main(),
|
* Real command line apps should use EepGet.main(),
|
||||||
* which has more options, and you don't have to wait for tunnels to be built.
|
* which has more options, and you don't have to wait for tunnels to be built.
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user