diff --git a/apps/i2psnark/java/src/org/klomp/snark/CompleteListener.java b/apps/i2psnark/java/src/org/klomp/snark/CompleteListener.java
index bd2f78e53..1d48654ba 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/CompleteListener.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/CompleteListener.java
@@ -54,8 +54,15 @@ public interface CompleteListener {
*/
public void gotPiece(Snark snark);
- // not really listeners but the easiest way to get back to an optional SnarkManager
+ /** not really listeners but the easiest way to get back to an optional SnarkManager */
public long getSavedTorrentTime(Snark snark);
public BitField getSavedTorrentBitField(Snark snark);
+ /**
+ * @since 0.9.15
+ */
public boolean getSavedPreserveNamesSetting(Snark snark);
+ /**
+ * @since 0.9.15
+ */
+ public long getSavedUploaded(Snark snark);
}
diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java
index 3dcb535cb..382ca4817 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java
@@ -281,6 +281,14 @@ class PeerCoordinator implements PeerListener
return uploaded;
}
+ /**
+ * Sets the initial total of uploaded bytes of all peers (from a saved status)
+ * @since 0.9.15
+ */
+ public void setUploaded(long up) {
+ uploaded = up;
+ }
+
/**
* Returns the total number of downloaded bytes of all peers.
*/
diff --git a/apps/i2psnark/java/src/org/klomp/snark/Snark.java b/apps/i2psnark/java/src/org/klomp/snark/Snark.java
index 9358623b7..740b7c2be 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/Snark.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/Snark.java
@@ -237,6 +237,7 @@ public class Snark
private volatile boolean _autoStoppable;
// String indicating main activity
private volatile String activity = "Not started";
+ private final long savedUploaded;
/**
@@ -463,6 +464,7 @@ public class Snark
trackerclient = new TrackerClient(meta, coordinator);
*/
+ savedUploaded = (completeListener != null) ? completeListener.getSavedUploaded(this) : 0;
if (start)
startTorrent();
}
@@ -488,6 +490,7 @@ public class Snark
this.infoHash = ih;
this.additionalTrackerURL = trackerURL;
this.rootDataDir = rootDir != null ? new File(rootDir) : null; // null only for FetchAndAdd extension
+ savedUploaded = 0;
stopped = true;
id = generateID();
@@ -556,6 +559,7 @@ public class Snark
_log.info("Starting PeerCoordinator, ConnectionAcceptor, and TrackerClient");
activity = "Collecting pieces";
coordinator = new PeerCoordinator(_util, id, infoHash, meta, storage, this, this);
+ coordinator.setUploaded(savedUploaded);
if (_peerCoordinatorSet != null) {
// multitorrent
_peerCoordinatorSet.add(coordinator);
@@ -619,7 +623,7 @@ public class Snark
pc.halt();
Storage st = storage;
if (st != null) {
- boolean changed = storage.isChanged();
+ boolean changed = storage.isChanged() || getUploaded() != savedUploaded;
try {
storage.close();
} catch (IOException ioe) {
@@ -773,7 +777,7 @@ public class Snark
PeerCoordinator coord = coordinator;
if (coord != null)
return coord.getUploaded();
- return 0;
+ return savedUploaded;
}
/**
diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
index b910e32d5..8ee32d985 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
@@ -92,6 +92,7 @@ public class SnarkManager implements CompleteListener {
private static final String PROP_META_BITFIELD = "bitfield";
private static final String PROP_META_PRIORITY = "priority";
private static final String PROP_META_PRESERVE_NAMES = "preserveFileNames";
+ private static final String PROP_META_UPLOADED = "uploaded";
//private static final String PROP_META_BITFIELD_SUFFIX = ".bitfield";
//private static final String PROP_META_PRIORITY_SUFFIX = ".priority";
private static final String PROP_META_MAGNET_PREFIX = "i2psnark.magnet.";
@@ -128,6 +129,9 @@ public class SnarkManager implements CompleteListener {
/**
* "name", "announceURL=websiteURL" pairs
* '=' in announceURL must be escaped as ,
+ *
+ * Please use host name, not b32 or full dest, in announce URL. Ensure in default hosts.txt.
+ * Please use host name, not b32 or full dest, in website URL. Ensure in default hosts.txt.
*/
private static final String DEFAULT_TRACKERS[] = {
// "Postman", "http://YRgrgTLGnbTq2aZOZDJQ~o6Uk5k6TK-OZtx0St9pb0G-5EGYURZioxqYG8AQt~LgyyI~NCj6aYWpPO-150RcEvsfgXLR~CxkkZcVpgt6pns8SRc3Bi-QSAkXpJtloapRGcQfzTtwllokbdC-aMGpeDOjYLd8b5V9Im8wdCHYy7LRFxhEtGb~RL55DA8aYOgEXcTpr6RPPywbV~Qf3q5UK55el6Kex-6VCxreUnPEe4hmTAbqZNR7Fm0hpCiHKGoToRcygafpFqDw5frLXToYiqs9d4liyVB-BcOb0ihORbo0nS3CLmAwZGvdAP8BZ7cIYE3Z9IU9D1G8JCMxWarfKX1pix~6pIA-sp1gKlL1HhYhPMxwyxvuSqx34o3BqU7vdTYwWiLpGM~zU1~j9rHL7x60pVuYaXcFQDR4-QVy26b6Pt6BlAZoFmHhPcAuWfu-SFhjyZYsqzmEmHeYdAwa~HojSbofg0TMUgESRXMw6YThK1KXWeeJVeztGTz25sL8AAAA.i2p/announce.php=http://tracker.postman.i2p/"
@@ -1357,7 +1361,7 @@ public class SnarkManager implements CompleteListener {
return false;
}
// so addTorrent won't recheck
- saveTorrentStatus(metainfo, bitfield, null, baseFile, true); // no file priorities
+ saveTorrentStatus(metainfo, bitfield, null, baseFile, true, 0); // no file priorities
try {
locked_writeMetaInfo(metainfo, filename, areFilesPublic());
// hold the lock for a long time
@@ -1522,6 +1526,21 @@ public class SnarkManager implements CompleteListener {
Properties config = getConfig(snark);
return Boolean.parseBoolean(config.getProperty(PROP_META_PRESERVE_NAMES));
}
+
+ /**
+ * Get setting for a torrent from the config file.
+ * @return setting, 0 if not found
+ * @since 0.9.15
+ */
+ public long getSavedUploaded(Snark snark) {
+ Properties config = getConfig(snark);
+ if (config != null) {
+ try {
+ return Long.parseLong(config.getProperty(PROP_META_UPLOADED));
+ } catch (NumberFormatException nfe) {}
+ }
+ return 0;
+ }
/**
* Save the completion status of a torrent and other data in the config file
@@ -1535,7 +1554,8 @@ public class SnarkManager implements CompleteListener {
if (meta == null || storage == null)
return;
saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities(),
- storage.getBase(), storage.getPreserveFileNames());
+ storage.getBase(), storage.getPreserveFileNames(),
+ snark.getUploaded());
}
/**
@@ -1550,14 +1570,14 @@ public class SnarkManager implements CompleteListener {
* @param base may be null
*/
private void saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities,
- File base, boolean preserveNames) {
+ File base, boolean preserveNames, long uploaded) {
synchronized (_configLock) {
- locked_saveTorrentStatus(metainfo, bitfield, priorities, base, preserveNames);
+ locked_saveTorrentStatus(metainfo, bitfield, priorities, base, preserveNames, uploaded);
}
}
private void locked_saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities,
- File base, boolean preserveNames) {
+ File base, boolean preserveNames, long uploaded) {
byte[] ih = metainfo.getInfoHash();
String bfs;
if (bitfield.complete()) {
@@ -1570,6 +1590,7 @@ public class SnarkManager implements CompleteListener {
config.setProperty(PROP_META_STAMP, Long.toString(System.currentTimeMillis()));
config.setProperty(PROP_META_BITFIELD, bfs);
config.setProperty(PROP_META_PRESERVE_NAMES, Boolean.toString(preserveNames));
+ config.setProperty(PROP_META_UPLOADED, Long.toString(uploaded));
if (base != null)
config.setProperty(PROP_META_BASE, base.getAbsolutePath());
@@ -1826,7 +1847,7 @@ public class SnarkManager implements CompleteListener {
Storage storage = snark.getStorage();
if (meta != null && storage != null)
saveTorrentStatus(meta, storage.getBitField(), storage.getFilePriorities(),
- storage.getBase(), storage.getPreserveFileNames());
+ storage.getBase(), storage.getPreserveFileNames(), snark.getUploaded());
}
/**
@@ -1849,7 +1870,7 @@ public class SnarkManager implements CompleteListener {
return null;
}
saveTorrentStatus(meta, storage.getBitField(), null,
- storage.getBase(), storage.getPreserveFileNames()); // no file priorities
+ storage.getBase(), storage.getPreserveFileNames(), 0);
// temp for addMessage() in case canonical throws
String name = storage.getBaseName();
try {
diff --git a/apps/i2psnark/java/src/org/klomp/snark/Storage.java b/apps/i2psnark/java/src/org/klomp/snark/Storage.java
index edcab93cb..04b9f7c11 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/Storage.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/Storage.java
@@ -316,15 +316,34 @@ public class Storage
}
/**
+ * Get index to pass to remaining(), getPriority(), setPriority()
+ *
* @param file non-canonical path (non-directory)
+ * @return internal index of file; -1 if unknown file
+ * @since 0.9.15
+ */
+ public int indexOf(File file) {
+ for (int i = 0; i < _torrentFiles.size(); i++) {
+ File f = _torrentFiles.get(i).RAFfile;
+ if (f.equals(file))
+ return i;
+ }
+ return -1;
+ }
+
+ /**
+ * @param fileIndex as obtained from indexOf
* @return number of bytes remaining; -1 if unknown file
* @since 0.7.14
*/
- public long remaining(File file) {
+ public long remaining(int fileIndex) {
+ if (fileIndex < 0 || fileIndex >= _torrentFiles.size())
+ return -1;
long bytes = 0;
- for (TorrentFile tf : _torrentFiles) {
- File f = tf.RAFfile;
- if (f.equals(file)) {
+ for (int i = 0; i < _torrentFiles.size(); i++) {
+ TorrentFile tf = _torrentFiles.get(i);
+ if (i == fileIndex) {
+ File f = tf.RAFfile;
if (complete())
return 0;
int psz = piece_size;
@@ -350,37 +369,30 @@ public class Storage
}
/**
- * @param file non-canonical path (non-directory)
+ * @param fileIndex as obtained from indexOf
* @since 0.8.1
*/
- public int getPriority(File file) {
+ public int getPriority(int fileIndex) {
if (complete() || metainfo.getFiles() == null)
return 0;
- for (TorrentFile tf : _torrentFiles) {
- File f = tf.RAFfile;
- if (f.equals(file))
- return tf.priority;
- }
- return 0;
+ if (fileIndex < 0 || fileIndex >= _torrentFiles.size())
+ return 0;
+ return _torrentFiles.get(fileIndex).priority;
}
/**
* Must call Snark.updatePiecePriorities()
* (which calls getPiecePriorities()) after calling this.
- * @param file non-canonical path (non-directory)
+ * @param fileIndex as obtained from indexOf
* @param pri default 0; <0 to disable
* @since 0.8.1
*/
- public void setPriority(File file, int pri) {
+ public void setPriority(int fileIndex, int pri) {
if (complete() || metainfo.getFiles() == null)
return;
- for (TorrentFile tf : _torrentFiles) {
- File f = tf.RAFfile;
- if (f.equals(file)) {
- tf.priority = pri;
- return;
- }
- }
+ if (fileIndex < 0 || fileIndex >= _torrentFiles.size())
+ return;
+ _torrentFiles.get(fileIndex).priority = pri;
}
/**
diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java
index c30ccb0cc..c9af416ec 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java
@@ -72,6 +72,8 @@ public class TrackerClient implements Runnable {
private static final String STOPPED_EVENT = "stopped";
private static final String NOT_REGISTERED = "torrent not registered"; //bytemonsoon
private static final String NOT_REGISTERED_2 = "torrent not found"; // diftracker
+ private static final String NOT_REGISTERED_3 = "torrent unauthorised"; // vuze
+ private static final String ERROR_GOT_HTML = "received html"; // fake return
/** this is our equivalent to router.utorrent.com for bootstrap */
private static final String DEFAULT_BACKUP_TRACKER = "http://tracker.welterde.i2p/a";
@@ -577,14 +579,20 @@ public class TrackerClient implements Runnable {
if (tr.isPrimary)
snark.setTrackerProblems(tr.trackerProblems);
String tplc = tr.trackerProblems.toLowerCase(Locale.US);
- if (tplc.startsWith(NOT_REGISTERED) || tplc.startsWith(NOT_REGISTERED_2)) {
+ if (tplc.startsWith(NOT_REGISTERED) || tplc.startsWith(NOT_REGISTERED_2) ||
+ tplc.startsWith(NOT_REGISTERED_3) || tplc.startsWith(ERROR_GOT_HTML)) {
// Give a guy some time to register it if using opentrackers too
//if (trckrs.size() == 1) {
// stop = true;
// snark.stopTorrent();
//} else { // hopefully each on the opentrackers list is really open
if (tr.registerFails++ > MAX_REGISTER_FAILS ||
+ !completed || // no use retrying if we aren't seeding
+ 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 + " : " +
+ tr.trackerProblems + " after " + tr.registerFails + " failures");
tr.stop = true;
//
}
@@ -797,10 +805,15 @@ public class TrackerClient implements Runnable {
tr.lastRequestTime = System.currentTimeMillis();
// Don't wait for a response to stopped when shutting down
boolean fast = _fastUnannounce && event.equals(STOPPED_EVENT);
- byte[] fetched = _util.get(s, true, fast ? -1 : 0, small ? 128 : 1024, small ? 1024 : 8*1024);
- if (fetched == null) {
- throw new IOException("Error fetching " + s);
- }
+ byte[] fetched = _util.get(s, true, fast ? -1 : 0, small ? 128 : 1024, small ? 1024 : 32*1024);
+ if (fetched == null)
+ throw new IOException("Error fetching");
+ if (fetched.length == 0)
+ throw new IOException("No data");
+ // The HTML check only works if we didn't exceed the maxium fetch size specified in get(),
+ // otherwise we already threw an IOE.
+ if (fetched[0] == '<')
+ throw new IOException(ERROR_GOT_HTML);
InputStream in = new ByteArrayInputStream(fetched);
diff --git a/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java b/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java
index db35a6910..d7f3b6690 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java
@@ -295,6 +295,10 @@ class UpdateRunner implements UpdateTask, CompleteListener {
return _smgr.getSavedPreserveNamesSetting(snark);
}
+ public long getSavedUploaded(Snark snark) {
+ return _smgr.getSavedUploaded(snark);
+ }
+
//////// end CompleteListener methods
private static String linkify(String url) {
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 b3e5f3038..b8ae5236f 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
@@ -25,8 +25,10 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import net.i2p.data.Base32;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
+import net.i2p.data.Hash;
import net.i2p.util.Log;
import org.klomp.snark.I2PSnarkUtil;
@@ -498,7 +500,7 @@ public class I2PSnarkServlet extends BasicServlet {
out.write(_("RX"));
out.write("\">");
out.write("\n
");
- if (_manager.util().connected() && !snarks.isEmpty()) {
+ if (!snarks.isEmpty()) {
out.write(" | ");
+ out.write(" onclick=\"document.location='" + encodedBaseName + "/';\">");
} else {
out.write('>');
}
@@ -1465,15 +1466,15 @@ public class I2PSnarkServlet extends BasicServlet {
// out.write("??"); // no meta size yet
out.write(" | \n\t");
out.write("");
- if(isRunning && isValid)
+ if (isValid && uploaded > 0)
out.write(formatSize(uploaded));
out.write(" | \n\t");
out.write("");
- if(isRunning && needed > 0)
+ if (isRunning && needed > 0)
out.write(formatSize(downBps) + "ps");
out.write(" | \n\t");
out.write("");
- if(isRunning && isValid)
+ if (isRunning && isValid)
out.write(formatSize(upBps) + "ps");
out.write(" | \n\t");
out.write("");
@@ -1717,8 +1718,10 @@ public class I2PSnarkServlet extends BasicServlet {
}
/**
- * Start of anchor only, caller must add anchor text or img and close anchor
- * @return string or null
+ * Generate link to details page if we know it supports it.
+ * Start of anchor only, caller must add anchor text or img and close anchor.
+ *
+ * @return string or null if unknown tracker
* @since 0.8.4
*/
private String getTrackerLinkUrl(String announce, byte[] infohash) {
@@ -1745,8 +1748,8 @@ public class I2PSnarkServlet extends BasicServlet {
}
/**
- * Full anchor with img
- * @return string or null
+ * Full link to details page with img
+ * @return string or null if details page unsupported
* @since 0.8.4
*/
private String getTrackerLink(String announce, byte[] infohash) {
@@ -1762,7 +1765,7 @@ public class I2PSnarkServlet extends BasicServlet {
}
/**
- * Full anchor with shortened URL as anchor text
+ * Full anchor to home page or details page with shortened host name as anchor text
* @return string, non-null
* @since 0.9.5
*/
@@ -1771,14 +1774,37 @@ public class I2PSnarkServlet extends BasicServlet {
String trackerLinkUrl = getTrackerLinkUrl(announce, infohash);
if (announce.startsWith("http://"))
announce = announce.substring(7);
+ // strip path
int slsh = announce.indexOf('/');
if (slsh > 0)
announce = announce.substring(0, slsh);
- if (trackerLinkUrl != null)
+ if (trackerLinkUrl != null) {
buf.append(trackerLinkUrl);
- else
- // TODO encode
- buf.append("");
+ } else {
+ // browsers don't like a full b64 dest, so convert it to b32
+ String host = announce;
+ if (host.length() >= 516) {
+ int colon = announce.indexOf(':');
+ String port = "";
+ if (colon > 0) {
+ port = host.substring(colon);
+ host = host.substring(0, colon);
+ }
+ if (host.endsWith(".i2p"))
+ host = host.substring(0, host.length() - 4);
+ byte[] b = Base64.decode(host);
+ if (b != null) {
+ Hash h = _context.sha().calculateHash(b);
+ // should we add the port back or strip it?
+ host = Base32.encode(h.getData()) + ".b32.i2p" + port;
+ }
+ }
+ buf.append("");
+ }
+ // strip port
+ int colon = announce.indexOf(':');
+ if (colon > 0)
+ announce = announce.substring(0, colon);
if (announce.length() > 67)
announce = DataHelper.escapeHTML(announce.substring(0, 40)) + "…" +
DataHelper.escapeHTML(announce.substring(announce.length() - 8));
@@ -2673,6 +2699,7 @@ public class I2PSnarkServlet extends BasicServlet {
boolean complete = false;
String status = "";
long length = item.length();
+ int fileIndex = -1;
int priority = 0;
if (item.isDirectory()) {
complete = true;
@@ -2684,8 +2711,9 @@ public class I2PSnarkServlet extends BasicServlet {
status = toImg("cancel") + ' ' + _("Torrent not found?");
} else {
Storage storage = snark.getStorage();
+ fileIndex = storage.indexOf(item);
- long remaining = storage.remaining(item);
+ long remaining = storage.remaining(fileIndex);
if (remaining < 0) {
complete = true;
status = toImg("cancel") + ' ' + _("File not found in torrent?");
@@ -2693,7 +2721,7 @@ public class I2PSnarkServlet extends BasicServlet {
complete = true;
status = toImg("tick") + ' ' + _("Complete");
} else {
- priority = storage.getPriority(item);
+ priority = storage.getPriority(fileIndex);
if (priority < 0)
status = toImg("cancel");
else if (priority == 0)
@@ -2745,17 +2773,17 @@ public class I2PSnarkServlet extends BasicServlet {
if (showPriority) {
buf.append(" | ");
if ((!complete) && (!item.isDirectory())) {
- buf.append("\n 0)
buf.append("checked=\"checked\"");
buf.append('>').append(_("High"));
- buf.append("\n').append(_("Normal"));
- buf.append("\n').append(_("Skip"));
@@ -2857,10 +2885,10 @@ public class I2PSnarkServlet extends BasicServlet {
String key = entry.getKey();
if (key.startsWith("pri.")) {
try {
- File file = new File(key.substring(4));
+ int fileIndex = Integer.parseInt(key.substring(4));
String val = entry.getValue()[0]; // jetty arrays
int pri = Integer.parseInt(val);
- storage.setPriority(file, pri);
+ storage.setPriority(fileIndex, pri);
//System.err.println("Priority now " + pri + " for " + file);
} catch (Throwable t) { t.printStackTrace(); }
}
diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/URIUtil.java b/apps/i2psnark/java/src/org/klomp/snark/web/URIUtil.java
index aae95b515..10f33b022 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/web/URIUtil.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/web/URIUtil.java
@@ -84,6 +84,7 @@ class URIUtil
case '<':
case '>':
case ' ':
+ case ':':
buf=new StringBuilder(path.length()*2);
break loop;
default:
@@ -139,6 +140,9 @@ class URIUtil
case 0x7f:
buf.append("%7F");
continue;
+ case ':':
+ buf.append("%3A");
+ continue;
default:
if (c <= 0x1f) // includes negative
toHex(c,buf);
@@ -183,6 +187,9 @@ class URIUtil
case ' ':
buf.append("%20");
continue;
+ case ':':
+ buf.append("%3A");
+ continue;
default:
if (c <= 0x1f || (c >= 0x7f && c <= 0x9f) || Character.isSpaceChar(c))
toHex(c,buf);
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java
index 128b1565d..ac1d2d39a 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java
@@ -182,6 +182,10 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
if (getTunnel() != tunnel)
return;
setupPostThrottle();
+ Properties props = tunnel.getClientOptions();
+ // see TunnelController.setSessionOptions()
+ String spoofHost = props.getProperty(TunnelController.PROP_SPOOFED_HOST);
+ _spoofHost = (spoofHost != null && spoofHost.trim().length() > 0) ? spoofHost.trim() : null;
super.optionsUpdated(tunnel);
}
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
index 0d2667d60..dfec31bc3 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
@@ -16,6 +16,7 @@ import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
+import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.util.Map;
import java.util.Properties;
@@ -50,8 +51,8 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
protected final Object slock = new Object();
protected final Object sslLock = new Object();
- protected final InetAddress remoteHost;
- protected final int remotePort;
+ protected InetAddress remoteHost;
+ protected int remotePort;
private final boolean _usePool;
protected final Logging l;
private I2PSSLSocketFactory _sslFactory;
@@ -265,6 +266,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
* Copy input stream to a byte array, so we can retry
* @since 0.7.10
*/
+/****
private static ByteArrayInputStream copyOfInputStream(InputStream is) throws IOException {
byte[] buf = new byte[128];
ByteArrayOutputStream os = new ByteArrayOutputStream(768);
@@ -279,6 +281,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
}
return new ByteArrayInputStream(os.toByteArray());
}
+****/
/**
* Start running the I2PTunnelServer.
@@ -348,6 +351,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
/**
* Update the I2PSocketManager.
+ * And since 0.9.15, the target host and port.
*
* @since 0.9.1
*/
@@ -357,6 +361,27 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
return;
Properties props = tunnel.getClientOptions();
sockMgr.setDefaultOptions(sockMgr.buildOptions(props));
+ // see TunnelController.setSessionOptions()
+ String h = props.getProperty(TunnelController.PROP_TARGET_HOST);
+ if (h != null) {
+ try {
+ remoteHost = InetAddress.getByName(h);
+ } catch (UnknownHostException uhe) {
+ l.log("Unknown host: " + h);
+ }
+ }
+ String p = props.getProperty(TunnelController.PROP_TARGET_PORT);
+ if (p != null) {
+ try {
+ int port = Integer.parseInt(p);
+ if (port > 0 && port <= 65535)
+ remotePort = port;
+ else
+ l.log("Bad port: " + port);
+ } catch (NumberFormatException nfe) {
+ l.log("Bad port: " + p);
+ }
+ }
buildSocketMap(props);
}
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
index 96fab6213..32b8b85af 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
@@ -487,6 +487,17 @@ public class TunnelController implements Logging {
String proxies = getProxyList();
if (proxies != null)
opts.setProperty(PROP_PROXIES, proxies);
+ // Ditto spoof host. Since 0.9.15.
+ String spoofhost = getSpoofedHost();
+ if (spoofhost != null)
+ opts.setProperty(PROP_SPOOFED_HOST, spoofhost);
+ // Ditto target host/port. Since 0.9.15.
+ String targethost = getTargetHost();
+ if (targethost != null)
+ opts.setProperty(PROP_TARGET_HOST, targethost);
+ String targetport = getTargetPort();
+ if (targetport != null)
+ opts.setProperty(PROP_TARGET_PORT, targetport);
_tunnel.setClientOptions(opts);
}
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
index cda31bdf6..30aee1eb4 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
@@ -258,6 +258,7 @@ public class IndexBean {
// give the messages a chance to make it to the window
try { Thread.sleep(1000); } catch (InterruptedException ie) {}
// and give them something to look at in any case
+ // FIXME name will be HTML escaped twice
return _("Starting tunnel") + ' ' + getTunnelName(_tunnel) + "...";
}
@@ -271,6 +272,7 @@ public class IndexBean {
// give the messages a chance to make it to the window
try { Thread.sleep(1000); } catch (InterruptedException ie) {}
// and give them something to look at in any case
+ // FIXME name will be HTML escaped twice
return _("Stopping tunnel") + ' ' + getTunnelName(_tunnel) + "...";
}
@@ -352,6 +354,7 @@ public class IndexBean {
List msgs = doSave();
if (ksMsg != null)
msgs.add(ksMsg);
+ // FIXME name will be HTML escaped twice
return getMessages(msgs);
}
@@ -402,7 +405,8 @@ public class IndexBean {
name = Long.toString(_context.clock().now());
}
}
- name = "i2ptunnel-deleted-" + name.replace(' ', '_') + '-' + _context.clock().now() + "-privkeys.dat";
+ name = name.replace(' ', '_').replace(':', '_').replace("..", "_").replace('/', '_').replace('\\', '_');
+ name = "i2ptunnel-deleted-" + name + '-' + _context.clock().now() + "-privkeys.dat";
File backupDir = new SecureFile(_context.getConfigDir(), TunnelController.KEY_BACKUP_DIR);
File to;
if (backupDir.isDirectory() || backupDir.mkdir())
@@ -451,13 +455,11 @@ public class IndexBean {
}
public boolean allowCSS() {
- String css = _context.getProperty(PROP_CSS_DISABLED);
- return (css == null);
+ return !_context.getBooleanProperty(PROP_CSS_DISABLED);
}
public boolean allowJS() {
- String js = _context.getProperty(PROP_JS_DISABLED);
- return (js == null);
+ return !_context.getBooleanProperty(PROP_JS_DISABLED);
}
public int getTunnelCount() {
@@ -727,8 +729,9 @@ public class IndexBean {
_name = (name != null ? name.trim() : null);
}
/** one line description */
- public void setDescription(String description) {
- _description = (description != null ? description.trim() : null);
+ public void setNofilter_description(String description) {
+ // '#' will blow up DataHelper.storeProps()
+ _description = (description != null ? description.replace('#', ' ').trim() : null);
}
/** I2CP host the router is on, ignored when in router context */
public void setClientHost(String host) {
diff --git a/apps/i2ptunnel/jsp/editClient.jsp b/apps/i2ptunnel/jsp/editClient.jsp
index 05c091b19..8fa03c96c 100644
--- a/apps/i2ptunnel/jsp/editClient.jsp
+++ b/apps/i2ptunnel/jsp/editClient.jsp
@@ -79,7 +79,7 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
-
+
@@ -341,6 +341,23 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
+
+
+
+
+
+
+ class="tickbox" />
+
+ <% } // !streamrclient %>
+
+
+
+
-
-
-
-
-
- class="tickbox" />
-
- <% } // !streamrclient %>
-
-
-
-
-
<% if ("client".equals(tunnelType) || "ircclient".equals(tunnelType) || "socksirctunnel".equals(tunnelType) || "sockstunnel".equals(tunnelType)) { %>
diff --git a/apps/i2ptunnel/jsp/wizard.jsp b/apps/i2ptunnel/jsp/wizard.jsp
index 0749bb54a..fe0f67a8b 100644
--- a/apps/i2ptunnel/jsp/wizard.jsp
+++ b/apps/i2ptunnel/jsp/wizard.jsp
@@ -233,11 +233,11 @@
<%=intl._("Description")%>:(E)
- " class="freetext" />
+ " class="freetext" />
<%
} else {
%> " />
- " /><%
+ " /><%
} /* curPage 3 */
/* End page 3 */ %>
@@ -484,7 +484,7 @@
-
+
<%
if (!"streamrclient".equals(tunnelType)) {
@@ -501,9 +501,9 @@
}
if ("httpclient".equals(tunnelType) || "connectclient".equals(tunnelType) || "sockstunnel".equals(tunnelType) || "socksirctunnel".equals(tunnelType)) {
%>
-
+
- <%
+ <%
}
if ("httpclient".equals(tunnelType)) {
%> updateSources = new ArrayList (2);
try {
+ // TODO SU3
updateSources.add(new URI(ConfigUpdateHelper.getNewsURL(_context)));
} catch (URISyntaxException use) {}
try {
+ // TODO
+ //updateSources.add(new URI(BACKUP_NEWS_URL_SU3));
updateSources.add(new URI(BACKUP_NEWS_URL));
} catch (URISyntaxException use) {}
UpdateRunner update = new NewsFetcher(_context, _mgr, updateSources);
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java
index 48343ff19..729a4b14d 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java
@@ -59,7 +59,7 @@ public class ConfigClientsHandler extends FormHandler {
}
if (_action.equals(_("Install Plugin"))) {
if (pluginsEnabled &&
- (_context.getBooleanProperty(ConfigClientsHelper.PROP_ENABLE_PLUGIN_INSTALL) ||
+ (_context.getBooleanPropertyDefaultTrue(ConfigClientsHelper.PROP_ENABLE_PLUGIN_INSTALL) ||
isAdvanced()))
installPlugin();
else
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java
index c9620cb7a..ab83bcbd2 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java
@@ -39,7 +39,12 @@ public class ConfigClientsHelper extends HelperBase {
/** @since 0.9.14.1 */
public boolean isPluginInstallEnabled() {
return PluginStarter.pluginsEnabled(_context) &&
- (_context.getBooleanProperty(PROP_ENABLE_PLUGIN_INSTALL) || isAdvanced());
+ (_context.getBooleanPropertyDefaultTrue(PROP_ENABLE_PLUGIN_INSTALL) || isAdvanced());
+ }
+
+ /** @since 0.9.15 */
+ public boolean isPluginUpdateEnabled() {
+ return !PluginStarter.getPlugins().isEmpty();
}
/** @since 0.8.3 */
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java
index 6ff7f3564..e33ab2bc7 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java
@@ -32,6 +32,7 @@ public class ConfigUpdateHandler extends FormHandler {
// public static final String DEFAULT_NEWS_URL = "http://dev.i2p.net/cgi-bin/cvsweb.cgi/i2p/news.xml?rev=HEAD";
public static final String OLD_DEFAULT_NEWS_URL = "http://complication.i2p/news.xml";
public static final String DEFAULT_NEWS_URL = "http://echelon.i2p/i2p/news.xml";
+ public static final String DEFAULT_NEWS_URL_SU3 = "http://echelon.i2p/i2p/news.su3";
public static final String PROP_REFRESH_FREQUENCY = "router.newsRefreshFrequency";
public static final long DEFAULT_REFRESH_FREQ = 36*60*60*1000l;
public static final String DEFAULT_REFRESH_FREQUENCY = Long.toString(DEFAULT_REFRESH_FREQ);
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHelper.java
index 4b7281fd9..4f5daab18 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHelper.java
@@ -32,6 +32,7 @@ public class ConfigUpdateHelper extends HelperBase {
/** hack to replace the old news location with the new one, even if they have saved
the update page at some point */
public static String getNewsURL(I2PAppContext ctx) {
+ // TODO SU3
String url = ctx.getProperty(ConfigUpdateHandler.PROP_NEWS_URL);
if (url != null && !url.equals(ConfigUpdateHandler.OLD_DEFAULT_NEWS_URL))
return url;
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java
index 062ab73d5..e3845df9d 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java
@@ -9,6 +9,7 @@ import java.util.ArrayList;
import java.util.List;
import net.i2p.I2PAppContext;
+import net.i2p.crypto.SigType;
import net.i2p.util.FileUtil;
import net.i2p.util.VersionComparator;
@@ -29,6 +30,17 @@ public class LogsHelper extends HelperBase {
return Server.getVersion();
}
+ /** @since 0.9.15 */
+ public String getUnavailableCrypto() {
+ StringBuilder buf = new StringBuilder(128);
+ for (SigType t : SigType.values()) {
+ if (!t.isAvailable()) {
+ buf.append("Crypto: ").append(t.toString()).append(" unavailable ");
+ }
+ }
+ return buf.toString();
+ }
+
/**
* Does not call logManager.flush(); call getCriticalLogs() first to flush
*/
diff --git a/apps/routerconsole/jsp/configclients.jsp b/apps/routerconsole/jsp/configclients.jsp
index 8bbfef188..a6d049ed7 100644
--- a/apps/routerconsole/jsp/configclients.jsp
+++ b/apps/routerconsole/jsp/configclients.jsp
@@ -105,7 +105,10 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
" />
-<% if (clientshelper.showPlugins()) { %>
+<%
+ if (clientshelper.showPlugins()) {
+ if (clientshelper.isPluginUpdateEnabled()) {
+%>
<%=intl._("Plugin Configuration")%>
<%=intl._("The plugins listed below are started by the webConsole client.")%>
@@ -116,25 +119,47 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
" />
" />
-
-<% if (clientshelper.isPluginInstallEnabled()) { %>
+<%
+ } // pluginUpdateEnabled
+ if (clientshelper.isPluginInstallEnabled()) {
+%>
<%=intl._("Plugin Installation")%>
<%=intl._("Look for available plugins on {0}.", "plugins.i2p")%>
<%=intl._("To install a plugin, enter the download URL:")%>
-
+
+<%
+ } // pluginInstallEnabled
+ if (clientshelper.isPluginInstallEnabled() || clientshelper.isPluginUpdateEnabled()) {
+%>
+
+
<%
- } // pluginInstallEnabled
- } // showPlugins
+ } // pluginInstallEnabled
+%>
+
+<%
+ if (clientshelper.isPluginUpdateEnabled()) {
+%>
+
+ " />
+
+<%
+ } // pluginUpdateEnabled
+%>
+
+<%
+ } // pluginInstallEnabled || pluginUpdateEnabled
+ } // showPlugins
%>
|