propagate from branch 'i2p.i2p.zzz.homepage' (head 27c8ea684056ce34ea81acdfd18571776ca63641)

to branch 'i2p.i2p' (head 83f37b19742045fa42ed71a0abd8e90d080c2c05)
This commit is contained in:
zzz
2012-03-03 19:05:15 +00:00
112 changed files with 1842 additions and 803 deletions

View File

@@ -56,6 +56,7 @@
<manifest> <manifest>
<attribute name="Main-Class" value="addressbook.Daemon"/> <attribute name="Main-Class" value="addressbook.Daemon"/>
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes}" /> <attribute name="Workspace-Changes" value="${workspace.changes}" />
@@ -75,6 +76,7 @@
<war basedir="${dist}/tmp" webxml="web.xml" destfile="${dist}/${war}"> <war basedir="${dist}/tmp" webxml="web.xml" destfile="${dist}/${war}">
<manifest> <manifest>
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" />

View File

Before

Width:  |  Height:  |  Size: 464 B

After

Width:  |  Height:  |  Size: 464 B

View File

Before

Width:  |  Height:  |  Size: 587 B

After

Width:  |  Height:  |  Size: 587 B

View File

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 673 B

View File

Before

Width:  |  Height:  |  Size: 882 B

After

Width:  |  Height:  |  Size: 882 B

View File

Before

Width:  |  Height:  |  Size: 889 B

After

Width:  |  Height:  |  Size: 889 B

View File

Before

Width:  |  Height:  |  Size: 766 B

After

Width:  |  Height:  |  Size: 766 B

View File

Before

Width:  |  Height:  |  Size: 653 B

After

Width:  |  Height:  |  Size: 653 B

View File

Before

Width:  |  Height:  |  Size: 537 B

After

Width:  |  Height:  |  Size: 537 B

View File

Before

Width:  |  Height:  |  Size: 578 B

After

Width:  |  Height:  |  Size: 578 B

View File

Before

Width:  |  Height:  |  Size: 591 B

After

Width:  |  Height:  |  Size: 591 B

View File

Before

Width:  |  Height:  |  Size: 385 B

After

Width:  |  Height:  |  Size: 385 B

View File

Before

Width:  |  Height:  |  Size: 853 B

After

Width:  |  Height:  |  Size: 853 B

View File

Before

Width:  |  Height:  |  Size: 635 B

After

Width:  |  Height:  |  Size: 635 B

View File

Before

Width:  |  Height:  |  Size: 294 B

After

Width:  |  Height:  |  Size: 294 B

View File

Before

Width:  |  Height:  |  Size: 591 B

After

Width:  |  Height:  |  Size: 591 B

View File

Before

Width:  |  Height:  |  Size: 589 B

After

Width:  |  Height:  |  Size: 589 B

View File

Before

Width:  |  Height:  |  Size: 591 B

After

Width:  |  Height:  |  Size: 591 B

View File

Before

Width:  |  Height:  |  Size: 537 B

After

Width:  |  Height:  |  Size: 537 B

View File

@@ -61,6 +61,7 @@
<attribute name="Main-Class" value="org.klomp.snark.Snark" /> <attribute name="Main-Class" value="org.klomp.snark.Snark" />
<attribute name="Class-Path" value="i2p.jar mstreaming.jar streaming.jar" /> <attribute name="Class-Path" value="i2p.jar mstreaming.jar streaming.jar" />
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" />
@@ -95,11 +96,16 @@
<target name="war" depends="jar, bundle, warUpToDate, listChangedFiles" unless="war.uptodate" > <target name="war" depends="jar, bundle, warUpToDate, listChangedFiles" unless="war.uptodate" >
<!-- set if unset --> <!-- set if unset -->
<property name="workspace.changes.tr" value="" /> <property name="workspace.changes.tr" value="" />
<war destfile="../i2psnark.war" webxml="../web.xml" basedir="../" includes="_icons/*" > <copy todir="build/icons/.icons" >
<fileset dir="../icons/" />
</copy>
<war destfile="../i2psnark.war" webxml="../web.xml" >
<!-- include only the web stuff, as of 0.7.12 the router will add i2psnark.jar to the classpath for the war --> <!-- include only the web stuff, as of 0.7.12 the router will add i2psnark.jar to the classpath for the war -->
<classes dir="./build/obj" includes="**/web/*.class" /> <classes dir="./build/obj" includes="**/web/*.class" />
<fileset dir="build/icons/" />
<manifest> <manifest>
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" />
@@ -109,7 +115,7 @@
<target name="warUpToDate"> <target name="warUpToDate">
<uptodate property="war.uptodate" targetfile="../i2psnark.war" > <uptodate property="war.uptodate" targetfile="../i2psnark.war" >
<srcfiles dir= "." includes="build/obj/org/klomp/snark/web/*.class ../_icons/* ../web.xml" /> <srcfiles dir= "." includes="build/obj/org/klomp/snark/web/*.class ../icons/* ../web.xml" />
</uptodate> </uptodate>
</target> </target>

View File

@@ -39,15 +39,19 @@ abstract class ExtensionHandler {
/** /**
* @param metasize -1 if unknown * @param metasize -1 if unknown
* @param pexAndMetadata advertise these capabilities
* @return bencoded outgoing handshake message * @return bencoded outgoing handshake message
*/ */
public static byte[] getHandshake(int metasize) { public static byte[] getHandshake(int metasize, boolean pexAndMetadata) {
Map<String, Object> handshake = new HashMap(); Map<String, Object> handshake = new HashMap();
Map<String, Integer> m = new HashMap(); Map<String, Integer> m = new HashMap();
m.put(TYPE_METADATA, Integer.valueOf(ID_METADATA)); if (pexAndMetadata) {
m.put(TYPE_PEX, Integer.valueOf(ID_PEX)); m.put(TYPE_METADATA, Integer.valueOf(ID_METADATA));
if (metasize >= 0) m.put(TYPE_PEX, Integer.valueOf(ID_PEX));
handshake.put("metadata_size", Integer.valueOf(metasize)); if (metasize >= 0)
handshake.put("metadata_size", Integer.valueOf(metasize));
}
// include the map even if empty so the far-end doesn't NPE
handshake.put("m", m); handshake.put("m", m);
handshake.put("p", Integer.valueOf(6881)); handshake.put("p", Integer.valueOf(6881));
handshake.put("v", "I2PSnark"); handshake.put("v", "I2PSnark");

View File

@@ -219,6 +219,10 @@ public class I2PSnarkUtil {
// opts.setProperty("i2p.streaming.writeTimeout", "90000"); // opts.setProperty("i2p.streaming.writeTimeout", "90000");
//if (opts.getProperty("i2p.streaming.readTimeout") == null) //if (opts.getProperty("i2p.streaming.readTimeout") == null)
// opts.setProperty("i2p.streaming.readTimeout", "120000"); // opts.setProperty("i2p.streaming.readTimeout", "120000");
if (opts.getProperty("i2p.streaming.maxConnsPerMinute") == null)
opts.setProperty("i2p.streaming.maxConnsPerMinute", "2");
if (opts.getProperty("i2p.streaming.maxTotalConnsPerMinute") == null)
opts.setProperty("i2p.streaming.maxTotalConnsPerMinute", "6");
_manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts); _manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
} }
// FIXME this only instantiates krpc once, left stuck with old manager // FIXME this only instantiates krpc once, left stuck with old manager

View File

@@ -61,6 +61,7 @@ public class MetaInfo
private final int piece_length; private final int piece_length;
private final byte[] piece_hashes; private final byte[] piece_hashes;
private final long length; private final long length;
private final boolean privateTorrent;
private Map<String, BEValue> infoMap; private Map<String, BEValue> infoMap;
/** /**
@@ -71,7 +72,7 @@ public class MetaInfo
* @param lengths null for single-file torrent * @param lengths null for single-file torrent
*/ */
MetaInfo(String announce, String name, String name_utf8, List<List<String>> files, List<Long> lengths, MetaInfo(String announce, String name, String name_utf8, List<List<String>> files, List<Long> lengths,
int piece_length, byte[] piece_hashes, long length) int piece_length, byte[] piece_hashes, long length, boolean privateTorrent)
{ {
this.announce = announce; this.announce = announce;
this.name = name; this.name = name;
@@ -82,6 +83,7 @@ public class MetaInfo
this.piece_length = piece_length; this.piece_length = piece_length;
this.piece_hashes = piece_hashes; this.piece_hashes = piece_hashes;
this.length = length; this.length = length;
this.privateTorrent = privateTorrent;
// TODO if we add a parameter for other keys // TODO if we add a parameter for other keys
//if (other != null) { //if (other != null) {
@@ -160,6 +162,10 @@ public class MetaInfo
else else
name_utf8 = null; name_utf8 = null;
// BEP 27
val = info.get("private");
privateTorrent = val != null && val.getString().equals("1");
val = info.get("piece length"); val = info.get("piece length");
if (val == null) if (val == null)
throw new InvalidBEncodingException("Missing piece length number"); throw new InvalidBEncodingException("Missing piece length number");
@@ -318,6 +324,14 @@ public class MetaInfo
return name; return name;
} }
/**
* Is it a private torrent?
* @since 0.9
*/
public boolean isPrivate() {
return privateTorrent;
}
/** /**
* Returns a list of lists of file name hierarchies or null if it is * Returns a list of lists of file name hierarchies or null if it is
* a single name. It has the same size as the list returned by * a single name. It has the same size as the list returned by
@@ -439,7 +453,7 @@ public class MetaInfo
{ {
return new MetaInfo(announce, name, name_utf8, files, return new MetaInfo(announce, name, name_utf8, files,
lengths, piece_length, lengths, piece_length,
piece_hashes, length); piece_hashes, length, privateTorrent);
} }
/** /**
@@ -475,6 +489,10 @@ public class MetaInfo
info.put("name", name); info.put("name", name);
if (name_utf8 != null) if (name_utf8 != null)
info.put("name.utf-8", name_utf8); info.put("name.utf-8", name_utf8);
// BEP 27
if (privateTorrent)
info.put("private", "1");
info.put("piece length", Integer.valueOf(piece_length)); info.put("piece length", Integer.valueOf(piece_length));
info.put("pieces", piece_hashes); info.put("pieces", piece_hashes);
if (files == null) if (files == null)

View File

@@ -268,7 +268,8 @@ public class Peer implements Comparable
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Peer supports extensions, sending reply message"); _log.debug("Peer supports extensions, sending reply message");
int metasize = metainfo != null ? metainfo.getInfoBytes().length : -1; int metasize = metainfo != null ? metainfo.getInfoBytes().length : -1;
out.sendExtension(0, ExtensionHandler.getHandshake(metasize)); boolean pexAndMetadata = metainfo == null || !metainfo.isPrivate();
out.sendExtension(0, ExtensionHandler.getHandshake(metasize, pexAndMetadata));
} }
if ((options & OPTION_I2P_DHT) != 0 && util.getDHT() != null) { if ((options & OPTION_I2P_DHT) != 0 && util.getDHT() != null) {

View File

@@ -1186,6 +1186,8 @@ public class PeerCoordinator implements PeerListener
* @since 0.8.4 * @since 0.8.4
*/ */
void sendPeers(Peer peer) { void sendPeers(Peer peer) {
if (metainfo != null && metainfo.isPrivate())
return;
Map<String, BEValue> handshake = peer.getHandshakeMap(); Map<String, BEValue> handshake = peer.getHandshakeMap();
if (handshake == null) if (handshake == null)
return; return;

View File

@@ -489,6 +489,13 @@ class PeerState implements DataLoader
/** @since 0.8.2 */ /** @since 0.8.2 */
void extensionMessage(int id, byte[] bs) void extensionMessage(int id, byte[] bs)
{ {
if (metainfo != null && metainfo.isPrivate() &&
(id == ExtensionHandler.ID_METADATA || id == ExtensionHandler.ID_PEX)) {
// shouldn't get this since we didn't advertise it but they could send it anyway
if (_log.shouldLog(Log.WARN))
_log.warn("Private torrent, ignoring ext msg " + id);
return;
}
ExtensionHandler.handleMessage(peer, listener, id, bs); ExtensionHandler.handleMessage(peer, listener, id, bs);
// Peer coord will get metadata from MagnetState, // Peer coord will get metadata from MagnetState,
// verify, and then call gotMetaInfo() // verify, and then call gotMetaInfo()

View File

@@ -8,6 +8,8 @@ import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@@ -35,8 +37,6 @@ import net.i2p.util.SecureFileOutputStream;
* Manage multiple snarks * Manage multiple snarks
*/ */
public class SnarkManager implements Snark.CompleteListener { public class SnarkManager implements Snark.CompleteListener {
private static SnarkManager _instance = new SnarkManager();
public static SnarkManager instance() { return _instance; }
/** /**
* Map of (canonical) filename of the .torrent file to Snark instance. * Map of (canonical) filename of the .torrent file to Snark instance.
@@ -57,6 +57,7 @@ public class SnarkManager implements Snark.CompleteListener {
private ConnectionAcceptor _connectionAcceptor; private ConnectionAcceptor _connectionAcceptor;
private Thread _monitor; private Thread _monitor;
private volatile boolean _running; private volatile boolean _running;
private final Map<String, String> _trackerMap;
public static final String PROP_I2CP_HOST = "i2psnark.i2cpHost"; public static final String PROP_I2CP_HOST = "i2psnark.i2cpHost";
public static final String PROP_I2CP_PORT = "i2psnark.i2cpPort"; public static final String PROP_I2CP_PORT = "i2psnark.i2cpPort";
@@ -89,6 +90,34 @@ public class SnarkManager implements Snark.CompleteListener {
public static final int DEFAULT_STARTUP_DELAY = 3; public static final int DEFAULT_STARTUP_DELAY = 3;
public static final int DEFAULT_REFRESH_DELAY_SECS = 60; public static final int DEFAULT_REFRESH_DELAY_SECS = 60;
/**
* "name", "announceURL=websiteURL" pairs
* '=' in announceURL must be escaped as &#44;
*/
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/"
// , "eBook", "http://E71FRom6PZNEqTN2Lr8P-sr23b7HJVC32KoGnVQjaX6zJiXwhJy2HsXob36Qmj81TYFZdewFZa9mSJ533UZgGyQkXo2ahctg82JKYZfDe5uDxAn1E9YPjxZCWJaFJh0S~UwSs~9AZ7UcauSJIoNtpxrtbmRNVFLqnkEDdLZi26TeucfOmiFmIWnVblLniWv3tG1boE9Abd-6j3FmYVrRucYuepAILYt6katmVNOk6sXmno1Eynrp~~MBuFq0Ko6~jsc2E2CRVYXDhGHEMdt-j6JUz5D7S2RIVzDRqQyAZLKJ7OdQDmI31przzmne1vOqqqLC~1xUumZVIvF~yOeJUGNjJ1Vx0J8i2BQIusn1pQJ6UCB~ZtZZLQtEb8EPVCfpeRi2ri1M5CyOuxN0V5ekmPHrYIBNevuTCRC26NP7ZS5VDgx1~NaC3A-CzJAE6f1QXi0wMI9aywNG5KGzOPifcsih8eyGyytvgLtrZtV7ykzYpPCS-rDfITncpn5hliPUAAAA.i2p/pub/bt/announce.php=http://de-ebook-archiv.i2p/pub/bt/"
// , "Gaytorrents", "http://uxPWHbK1OIj9HxquaXuhMiIvi21iK0~ZiG9d8G0840ZXIg0r6CbiV71xlsqmdnU6wm0T2LySriM0doW2gUigo-5BNkUquHwOjLROiETnB3ZR0Ml4IGa6QBPn1aAq2d9~g1r1nVjLE~pcFnXB~cNNS7kIhX1d6nLgYVZf0C2cZopEow2iWVUggGGnAA9mHjE86zLEnTvAyhbAMTqDQJhEuLa0ZYSORqzJDMkQt90MV4YMjX1ICY6RfUSFmxEqu0yWTrkHsTtRw48l~dz9wpIgc0a0T9C~eeWvmBFTqlJPtQZwntpNeH~jF7nlYzB58olgV2HHFYpVYD87DYNzTnmNWxCJ5AfDorm6AIUCV2qaE7tZtI1h6fbmGpGlPyW~Kw5GXrRfJwNvr6ajwAVi~bPVnrBwDZezHkfW4slOO8FACPR28EQvaTu9nwhAbqESxV2hCTq6vQSGjuxHeOuzBOEvRWkLKOHWTC09t2DbJ94FSqETmZopTB1ukEmaxRWbKSIaAAAA.i2p/announce.php=http://gaytorrents.i2p/"
// , "NickyB", "http://9On6d3cZ27JjwYCtyJJbowe054d5tFnfMjv4PHsYs-EQn4Y4mk2zRixatvuAyXz2MmRfXG-NAUfhKr0KCxRNZbvHmlckYfT-WBzwwpiMAl0wDFY~Pl8cqXuhfikSG5WrqdPfDNNIBuuznS0dqaczf~OyVaoEOpvuP3qV6wKqbSSLpjOwwAaQPHjlRtNIW8-EtUZp-I0LT45HSoowp~6b7zYmpIyoATvIP~sT0g0MTrczWhbVTUZnEkZeLhOR0Duw1-IRXI2KHPbA24wLO9LdpKKUXed05RTz0QklW5ROgR6TYv7aXFufX8kC0-DaKvQ5JKG~h8lcoHvm1RCzNqVE-2aiZnO2xH08H-iCWoLNJE-Td2kT-Tsc~3QdQcnEUcL5BF-VT~QYRld2--9r0gfGl-yDrJZrlrihHGr5J7ImahelNn9PpkVp6eIyABRmJHf2iicrk3CtjeG1j9OgTSwaNmEpUpn4aN7Kx0zNLdH7z6uTgCGD9Kmh1MFYrsoNlTp4AAAA.i2p/bittorrent/announce.php=http://nickyb.i2p/bittorrent/"
// , "Orion", "http://gKik1lMlRmuroXVGTZ~7v4Vez3L3ZSpddrGZBrxVriosCQf7iHu6CIk8t15BKsj~P0JJpxrofeuxtm7SCUAJEr0AIYSYw8XOmp35UfcRPQWyb1LsxUkMT4WqxAT3s1ClIICWlBu5An~q-Mm0VFlrYLIPBWlUFnfPR7jZ9uP5ZMSzTKSMYUWao3ejiykr~mtEmyls6g-ZbgKZawa9II4zjOy-hdxHgP-eXMDseFsrym4Gpxvy~3Fv9TuiSqhpgm~UeTo5YBfxn6~TahKtE~~sdCiSydqmKBhxAQ7uT9lda7xt96SS09OYMsIWxLeQUWhns-C~FjJPp1D~IuTrUpAFcVEGVL-BRMmdWbfOJEcWPZ~CBCQSO~VkuN1ebvIOr9JBerFMZSxZtFl8JwcrjCIBxeKPBmfh~xYh16BJm1BBBmN1fp2DKmZ2jBNkAmnUbjQOqWvUcehrykWk5lZbE7bjJMDFH48v3SXwRuDBiHZmSbsTY6zhGY~GkMQHNGxPMMSIAAAA.i2p/bt/announce.php=http://orion.i2p/bt/"
// , "anonymity", "http://8EoJZIKrWgGuDrxA3nRJs1jsPfiGwmFWL91hBrf0HA7oKhEvAna4Ocx47VLUR9retVEYBAyWFK-eZTPcvhnz9XffBEiJQQ~kFSCqb1fV6IfPiV3HySqi9U5Caf6~hC46fRd~vYnxmaBLICT3N160cxBETqH3v2rdxdJpvYt8q4nMk9LUeVXq7zqCTFLLG5ig1uKgNzBGe58iNcsvTEYlnbYcE930ABmrzj8G1qQSgSwJ6wx3tUQNl1z~4wSOUMan~raZQD60lRK70GISjoX0-D0Po9WmPveN3ES3g72TIET3zc3WPdK2~lgmKGIs8GgNLES1cXTolvbPhdZK1gxddRMbJl6Y6IPFyQ9o4-6Rt3Lp-RMRWZ2TG7j2OMcNSiOmATUhKEFBDfv-~SODDyopGBmfeLw16F4NnYednvn4qP10dyMHcUASU6Zag4mfc2-WivrOqeWhD16fVAh8MoDpIIT~0r9XmwdaVFyLcjbXObabJczxCAW3fodQUnvuSkwzAAAA.i2p/anonymityTracker/announce.php=http://anonymityweb.i2p/anonymityTracker/"
// , "The freak's tracker", "http://mHKva9x24E5Ygfey2llR1KyQHv5f8hhMpDMwJDg1U-hABpJ2NrQJd6azirdfaR0OKt4jDlmP2o4Qx0H598~AteyD~RJU~xcWYdcOE0dmJ2e9Y8-HY51ie0B1yD9FtIV72ZI-V3TzFDcs6nkdX9b81DwrAwwFzx0EfNvK1GLVWl59Ow85muoRTBA1q8SsZImxdyZ-TApTVlMYIQbdI4iQRwU9OmmtefrCe~ZOf4UBS9-KvNIqUL0XeBSqm0OU1jq-D10Ykg6KfqvuPnBYT1BYHFDQJXW5DdPKwcaQE4MtAdSGmj1epDoaEBUa9btQlFsM2l9Cyn1hzxqNWXELmx8dRlomQLlV4b586dRzW~fLlOPIGC13ntPXogvYvHVyEyptXkv890jC7DZNHyxZd5cyrKC36r9huKvhQAmNABT2Y~pOGwVrb~RpPwT0tBuPZ3lHYhBFYmD8y~AOhhNHKMLzea1rfwTvovBMByDdFps54gMN1mX4MbCGT4w70vIopS9yAAAA.i2p/bytemonsoon/announce.php"
// , "mastertracker", "http://VzXD~stRKbL3MOmeTn1iaCQ0CFyTmuFHiKYyo0Rd~dFPZFCYH-22rT8JD7i-C2xzYFa4jT5U2aqHzHI-Jre4HL3Ri5hFtZrLk2ax3ji7Qfb6qPnuYkuiF2E2UDmKUOppI8d9Ye7tjdhQVCy0izn55tBaB-U7UWdcvSK2i85sauyw3G0Gfads1Rvy5-CAe2paqyYATcDmGjpUNLoxbfv9KH1KmwRTNH6k1v4PyWYYnhbT39WfKMbBjSxVQRdi19cyJrULSWhjxaQfJHeWx5Z8Ev4bSPByBeQBFl2~4vqy0S5RypINsRSa3MZdbiAAyn5tr5slWR6QdoqY3qBQgBJFZppy-3iWkFqqKgSxCPundF8gdDLC5ddizl~KYcYKl42y9SGFHIukH-TZs8~em0~iahzsqWVRks3zRG~tlBcX2U3M2~OJs~C33-NKhyfZT7-XFBREvb8Szmd~p66jDxrwOnKaku-G6DyoQipJqIz4VHmY9-y5T8RrUcJcM-5lVoMpAAAA.i2p/announce.php=http://tracker.mastertracker.i2p/"
// , "Galen", "http://5jpwQMI5FT303YwKa5Rd38PYSX04pbIKgTaKQsWbqoWjIfoancFdWCShXHLI5G5ofOb0Xu11vl2VEMyPsg1jUFYSVnu4-VfMe3y4TKTR6DTpetWrnmEK6m2UXh91J5DZJAKlgmO7UdsFlBkQfR2rY853-DfbJtQIFl91tbsmjcA5CGQi4VxMFyIkBzv-pCsuLQiZqOwWasTlnzey8GcDAPG1LDcvfflGV~6F5no9mnuisZPteZKlrv~~TDoXTj74QjByWc4EOYlwqK8sbU9aOvz~s31XzErbPTfwiawiaZ0RUI-IDrKgyvmj0neuFTWgjRGVTH8bz7cBZIc3viy6ioD-eMQOrXaQL0TCWZUelRwHRvgdPiQrxdYQs7ixkajeHzxi-Pq0EMm5Vbh3j3Q9kfUFW3JjFDA-MLB4g6XnjCbM5J1rC0oOBDCIEfhQkszru5cyLjHiZ5yeA0VThgu~c7xKHybv~OMXION7V8pBKOgET7ZgAkw1xgYe3Kkyq5syAAAA.i2p/tr/announce.php=http://galen.i2p/tr/"
"Postman", "http://tracker2.postman.i2p/announce.php=http://tracker2.postman.i2p/"
,"Welterde", "http://tracker.welterde.i2p/a=http://tracker.welterde.i2p/stats?mode=top5"
,"Diftracker", "http://n--XWjHjUPjnMNrSwXA2OYXpMIUL~u4FNXnrt2HtjK3y6j~4SOClyyeKzd0zRPlixxkCe2wfBIYye3bZsaqAD8bd0QMmowxbq91WpjsPfKMiphJbePKXtYAVARiy0cqyvh1d2LyDE-6wkvgaw45hknmS0U-Dg3YTJZbAQRU2SKXgIlAbWCv4R0kDFBLEVpReDiJef3rzAWHiW8yjmJuJilkYjMwlfRjw8xx1nl2s~yhlljk1pl13jGYb0nfawQnuOWeP-ASQWvAAyVgKvZRJE2O43S7iveu9piuv7plXWbt36ef7ndu2GNoNyPOBdpo9KUZ-NOXm4Kgh659YtEibL15dEPAOdxprY0sYUurVw8OIWqrpX7yn08nbi6qHVGqQwTpxH35vkL8qrCbm-ym7oQJQnNmSDrNTyWYRFSq5s5~7DAdFDzqRPW-pX~g0zEivWj5tzkhvG9rVFgFo0bpQX3X0PUAV9Xbyf8u~v8Zbr9K1pCPqBq9XEr4TqaLHw~bfAAAA.i2p/announce.php=http://diftracker.i2p/"
// , "CRSTRACK", "http://b4G9sCdtfvccMAXh~SaZrPqVQNyGQbhbYMbw6supq2XGzbjU4NcOmjFI0vxQ8w1L05twmkOvg5QERcX6Mi8NQrWnR0stLExu2LucUXg1aYjnggxIR8TIOGygZVIMV3STKH4UQXD--wz0BUrqaLxPhrm2Eh9Hwc8TdB6Na4ShQUq5Xm8D4elzNUVdpM~RtChEyJWuQvoGAHY3ppX-EJJLkiSr1t77neS4Lc-KofMVmgI9a2tSSpNAagBiNI6Ak9L1T0F9uxeDfEG9bBSQPNMOSUbAoEcNxtt7xOW~cNOAyMyGydwPMnrQ5kIYPY8Pd3XudEko970vE0D6gO19yoBMJpKx6Dh50DGgybLQ9CpRaynh2zPULTHxm8rneOGRcQo8D3mE7FQ92m54~SvfjXjD2TwAVGI~ae~n9HDxt8uxOecAAvjjJ3TD4XM63Q9TmB38RmGNzNLDBQMEmJFpqQU8YeuhnS54IVdUoVQFqui5SfDeLXlSkh4vYoMU66pvBfWbAAAA.i2p/tracker/announce.php=http://crstrack.i2p/tracker/"
// ,"Exotrack", "http://blbgywsjubw3d2zih2giokakhe3o2cko7jtte4risb3hohbcoyva.b32.i2p/announce.php=http://exotrack.i2p/"
};
/** comma delimited list of name=announceURL=baseURL for the trackers to be displayed */
public static final String PROP_TRACKERS = "i2psnark.trackers";
private static final SnarkManager _instance = new SnarkManager();
public static SnarkManager instance() { return _instance; }
private SnarkManager() { private SnarkManager() {
_snarks = new ConcurrentHashMap(); _snarks = new ConcurrentHashMap();
_magnets = new ConcurrentHashSet(); _magnets = new ConcurrentHashSet();
@@ -100,6 +129,7 @@ public class SnarkManager implements Snark.CompleteListener {
_configFile = new File(CONFIG_FILE); _configFile = new File(CONFIG_FILE);
if (!_configFile.isAbsolute()) if (!_configFile.isAbsolute())
_configFile = new File(_context.getConfigDir(), CONFIG_FILE); _configFile = new File(_context.getConfigDir(), CONFIG_FILE);
_trackerMap = Collections.synchronizedMap(new TreeMap(new IgnoreCaseComparator()));
loadConfig(null); loadConfig(null);
} }
@@ -312,6 +342,7 @@ public class SnarkManager implements Snark.CompleteListener {
boolean bOT = useOT == null || Boolean.valueOf(useOT).booleanValue(); boolean bOT = useOT == null || Boolean.valueOf(useOT).booleanValue();
_util.setUseOpenTrackers(bOT); _util.setUseOpenTrackers(bOT);
getDataDir().mkdirs(); getDataDir().mkdirs();
initTrackerMap();
} }
private int getInt(String prop, int defaultVal) { private int getInt(String prop, int defaultVal) {
@@ -663,7 +694,9 @@ public class SnarkManager implements Snark.CompleteListener {
} }
if (!TrackerClient.isValidAnnounce(info.getAnnounce())) { if (!TrackerClient.isValidAnnounce(info.getAnnounce())) {
if (_util.shouldUseOpenTrackers() && _util.getOpenTrackers() != null) { if (info.isPrivate()) {
addMessage(_("ERROR - No I2P trackers in private torrent \"{0}\"", info.getName()));
} else if (_util.shouldUseOpenTrackers() && _util.getOpenTrackers() != null) {
//addMessage(_("Warning - No I2P trackers in \"{0}\", will announce to I2P open trackers and DHT only.", info.getName())); //addMessage(_("Warning - No I2P trackers in \"{0}\", will announce to I2P open trackers and DHT only.", info.getName()));
addMessage(_("Warning - No I2P trackers in \"{0}\", will announce to I2P open trackers only.", info.getName())); addMessage(_("Warning - No I2P trackers in \"{0}\", will announce to I2P open trackers only.", info.getName()));
//} else if (_util.getDHT() != null) { //} else if (_util.getDHT() != null) {
@@ -1356,57 +1389,59 @@ public class SnarkManager implements Snark.CompleteListener {
} }
/** /**
* "name", "announceURL=websiteURL" pairs * Sorted map of name to announceURL=baseURL
* Modifiable, not a copy
*/ */
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/"
// , "eBook", "http://E71FRom6PZNEqTN2Lr8P-sr23b7HJVC32KoGnVQjaX6zJiXwhJy2HsXob36Qmj81TYFZdewFZa9mSJ533UZgGyQkXo2ahctg82JKYZfDe5uDxAn1E9YPjxZCWJaFJh0S~UwSs~9AZ7UcauSJIoNtpxrtbmRNVFLqnkEDdLZi26TeucfOmiFmIWnVblLniWv3tG1boE9Abd-6j3FmYVrRucYuepAILYt6katmVNOk6sXmno1Eynrp~~MBuFq0Ko6~jsc2E2CRVYXDhGHEMdt-j6JUz5D7S2RIVzDRqQyAZLKJ7OdQDmI31przzmne1vOqqqLC~1xUumZVIvF~yOeJUGNjJ1Vx0J8i2BQIusn1pQJ6UCB~ZtZZLQtEb8EPVCfpeRi2ri1M5CyOuxN0V5ekmPHrYIBNevuTCRC26NP7ZS5VDgx1~NaC3A-CzJAE6f1QXi0wMI9aywNG5KGzOPifcsih8eyGyytvgLtrZtV7ykzYpPCS-rDfITncpn5hliPUAAAA.i2p/pub/bt/announce.php=http://de-ebook-archiv.i2p/pub/bt/"
// , "Gaytorrents", "http://uxPWHbK1OIj9HxquaXuhMiIvi21iK0~ZiG9d8G0840ZXIg0r6CbiV71xlsqmdnU6wm0T2LySriM0doW2gUigo-5BNkUquHwOjLROiETnB3ZR0Ml4IGa6QBPn1aAq2d9~g1r1nVjLE~pcFnXB~cNNS7kIhX1d6nLgYVZf0C2cZopEow2iWVUggGGnAA9mHjE86zLEnTvAyhbAMTqDQJhEuLa0ZYSORqzJDMkQt90MV4YMjX1ICY6RfUSFmxEqu0yWTrkHsTtRw48l~dz9wpIgc0a0T9C~eeWvmBFTqlJPtQZwntpNeH~jF7nlYzB58olgV2HHFYpVYD87DYNzTnmNWxCJ5AfDorm6AIUCV2qaE7tZtI1h6fbmGpGlPyW~Kw5GXrRfJwNvr6ajwAVi~bPVnrBwDZezHkfW4slOO8FACPR28EQvaTu9nwhAbqESxV2hCTq6vQSGjuxHeOuzBOEvRWkLKOHWTC09t2DbJ94FSqETmZopTB1ukEmaxRWbKSIaAAAA.i2p/announce.php=http://gaytorrents.i2p/"
// , "NickyB", "http://9On6d3cZ27JjwYCtyJJbowe054d5tFnfMjv4PHsYs-EQn4Y4mk2zRixatvuAyXz2MmRfXG-NAUfhKr0KCxRNZbvHmlckYfT-WBzwwpiMAl0wDFY~Pl8cqXuhfikSG5WrqdPfDNNIBuuznS0dqaczf~OyVaoEOpvuP3qV6wKqbSSLpjOwwAaQPHjlRtNIW8-EtUZp-I0LT45HSoowp~6b7zYmpIyoATvIP~sT0g0MTrczWhbVTUZnEkZeLhOR0Duw1-IRXI2KHPbA24wLO9LdpKKUXed05RTz0QklW5ROgR6TYv7aXFufX8kC0-DaKvQ5JKG~h8lcoHvm1RCzNqVE-2aiZnO2xH08H-iCWoLNJE-Td2kT-Tsc~3QdQcnEUcL5BF-VT~QYRld2--9r0gfGl-yDrJZrlrihHGr5J7ImahelNn9PpkVp6eIyABRmJHf2iicrk3CtjeG1j9OgTSwaNmEpUpn4aN7Kx0zNLdH7z6uTgCGD9Kmh1MFYrsoNlTp4AAAA.i2p/bittorrent/announce.php=http://nickyb.i2p/bittorrent/"
// , "Orion", "http://gKik1lMlRmuroXVGTZ~7v4Vez3L3ZSpddrGZBrxVriosCQf7iHu6CIk8t15BKsj~P0JJpxrofeuxtm7SCUAJEr0AIYSYw8XOmp35UfcRPQWyb1LsxUkMT4WqxAT3s1ClIICWlBu5An~q-Mm0VFlrYLIPBWlUFnfPR7jZ9uP5ZMSzTKSMYUWao3ejiykr~mtEmyls6g-ZbgKZawa9II4zjOy-hdxHgP-eXMDseFsrym4Gpxvy~3Fv9TuiSqhpgm~UeTo5YBfxn6~TahKtE~~sdCiSydqmKBhxAQ7uT9lda7xt96SS09OYMsIWxLeQUWhns-C~FjJPp1D~IuTrUpAFcVEGVL-BRMmdWbfOJEcWPZ~CBCQSO~VkuN1ebvIOr9JBerFMZSxZtFl8JwcrjCIBxeKPBmfh~xYh16BJm1BBBmN1fp2DKmZ2jBNkAmnUbjQOqWvUcehrykWk5lZbE7bjJMDFH48v3SXwRuDBiHZmSbsTY6zhGY~GkMQHNGxPMMSIAAAA.i2p/bt/announce.php=http://orion.i2p/bt/"
// , "anonymity", "http://8EoJZIKrWgGuDrxA3nRJs1jsPfiGwmFWL91hBrf0HA7oKhEvAna4Ocx47VLUR9retVEYBAyWFK-eZTPcvhnz9XffBEiJQQ~kFSCqb1fV6IfPiV3HySqi9U5Caf6~hC46fRd~vYnxmaBLICT3N160cxBETqH3v2rdxdJpvYt8q4nMk9LUeVXq7zqCTFLLG5ig1uKgNzBGe58iNcsvTEYlnbYcE930ABmrzj8G1qQSgSwJ6wx3tUQNl1z~4wSOUMan~raZQD60lRK70GISjoX0-D0Po9WmPveN3ES3g72TIET3zc3WPdK2~lgmKGIs8GgNLES1cXTolvbPhdZK1gxddRMbJl6Y6IPFyQ9o4-6Rt3Lp-RMRWZ2TG7j2OMcNSiOmATUhKEFBDfv-~SODDyopGBmfeLw16F4NnYednvn4qP10dyMHcUASU6Zag4mfc2-WivrOqeWhD16fVAh8MoDpIIT~0r9XmwdaVFyLcjbXObabJczxCAW3fodQUnvuSkwzAAAA.i2p/anonymityTracker/announce.php=http://anonymityweb.i2p/anonymityTracker/"
// , "The freak's tracker", "http://mHKva9x24E5Ygfey2llR1KyQHv5f8hhMpDMwJDg1U-hABpJ2NrQJd6azirdfaR0OKt4jDlmP2o4Qx0H598~AteyD~RJU~xcWYdcOE0dmJ2e9Y8-HY51ie0B1yD9FtIV72ZI-V3TzFDcs6nkdX9b81DwrAwwFzx0EfNvK1GLVWl59Ow85muoRTBA1q8SsZImxdyZ-TApTVlMYIQbdI4iQRwU9OmmtefrCe~ZOf4UBS9-KvNIqUL0XeBSqm0OU1jq-D10Ykg6KfqvuPnBYT1BYHFDQJXW5DdPKwcaQE4MtAdSGmj1epDoaEBUa9btQlFsM2l9Cyn1hzxqNWXELmx8dRlomQLlV4b586dRzW~fLlOPIGC13ntPXogvYvHVyEyptXkv890jC7DZNHyxZd5cyrKC36r9huKvhQAmNABT2Y~pOGwVrb~RpPwT0tBuPZ3lHYhBFYmD8y~AOhhNHKMLzea1rfwTvovBMByDdFps54gMN1mX4MbCGT4w70vIopS9yAAAA.i2p/bytemonsoon/announce.php"
// , "mastertracker", "http://VzXD~stRKbL3MOmeTn1iaCQ0CFyTmuFHiKYyo0Rd~dFPZFCYH-22rT8JD7i-C2xzYFa4jT5U2aqHzHI-Jre4HL3Ri5hFtZrLk2ax3ji7Qfb6qPnuYkuiF2E2UDmKUOppI8d9Ye7tjdhQVCy0izn55tBaB-U7UWdcvSK2i85sauyw3G0Gfads1Rvy5-CAe2paqyYATcDmGjpUNLoxbfv9KH1KmwRTNH6k1v4PyWYYnhbT39WfKMbBjSxVQRdi19cyJrULSWhjxaQfJHeWx5Z8Ev4bSPByBeQBFl2~4vqy0S5RypINsRSa3MZdbiAAyn5tr5slWR6QdoqY3qBQgBJFZppy-3iWkFqqKgSxCPundF8gdDLC5ddizl~KYcYKl42y9SGFHIukH-TZs8~em0~iahzsqWVRks3zRG~tlBcX2U3M2~OJs~C33-NKhyfZT7-XFBREvb8Szmd~p66jDxrwOnKaku-G6DyoQipJqIz4VHmY9-y5T8RrUcJcM-5lVoMpAAAA.i2p/announce.php=http://tracker.mastertracker.i2p/"
// , "Galen", "http://5jpwQMI5FT303YwKa5Rd38PYSX04pbIKgTaKQsWbqoWjIfoancFdWCShXHLI5G5ofOb0Xu11vl2VEMyPsg1jUFYSVnu4-VfMe3y4TKTR6DTpetWrnmEK6m2UXh91J5DZJAKlgmO7UdsFlBkQfR2rY853-DfbJtQIFl91tbsmjcA5CGQi4VxMFyIkBzv-pCsuLQiZqOwWasTlnzey8GcDAPG1LDcvfflGV~6F5no9mnuisZPteZKlrv~~TDoXTj74QjByWc4EOYlwqK8sbU9aOvz~s31XzErbPTfwiawiaZ0RUI-IDrKgyvmj0neuFTWgjRGVTH8bz7cBZIc3viy6ioD-eMQOrXaQL0TCWZUelRwHRvgdPiQrxdYQs7ixkajeHzxi-Pq0EMm5Vbh3j3Q9kfUFW3JjFDA-MLB4g6XnjCbM5J1rC0oOBDCIEfhQkszru5cyLjHiZ5yeA0VThgu~c7xKHybv~OMXION7V8pBKOgET7ZgAkw1xgYe3Kkyq5syAAAA.i2p/tr/announce.php=http://galen.i2p/tr/"
"Postman", "http://tracker2.postman.i2p/announce.php=http://tracker2.postman.i2p/"
,"Welterde", "http://tracker.welterde.i2p/a=http://tracker.welterde.i2p/stats?mode=top5"
,"Diftracker", "http://n--XWjHjUPjnMNrSwXA2OYXpMIUL~u4FNXnrt2HtjK3y6j~4SOClyyeKzd0zRPlixxkCe2wfBIYye3bZsaqAD8bd0QMmowxbq91WpjsPfKMiphJbePKXtYAVARiy0cqyvh1d2LyDE-6wkvgaw45hknmS0U-Dg3YTJZbAQRU2SKXgIlAbWCv4R0kDFBLEVpReDiJef3rzAWHiW8yjmJuJilkYjMwlfRjw8xx1nl2s~yhlljk1pl13jGYb0nfawQnuOWeP-ASQWvAAyVgKvZRJE2O43S7iveu9piuv7plXWbt36ef7ndu2GNoNyPOBdpo9KUZ-NOXm4Kgh659YtEibL15dEPAOdxprY0sYUurVw8OIWqrpX7yn08nbi6qHVGqQwTpxH35vkL8qrCbm-ym7oQJQnNmSDrNTyWYRFSq5s5~7DAdFDzqRPW-pX~g0zEivWj5tzkhvG9rVFgFo0bpQX3X0PUAV9Xbyf8u~v8Zbr9K1pCPqBq9XEr4TqaLHw~bfAAAA.i2p/announce.php=http://diftracker.i2p/"
// , "CRSTRACK", "http://b4G9sCdtfvccMAXh~SaZrPqVQNyGQbhbYMbw6supq2XGzbjU4NcOmjFI0vxQ8w1L05twmkOvg5QERcX6Mi8NQrWnR0stLExu2LucUXg1aYjnggxIR8TIOGygZVIMV3STKH4UQXD--wz0BUrqaLxPhrm2Eh9Hwc8TdB6Na4ShQUq5Xm8D4elzNUVdpM~RtChEyJWuQvoGAHY3ppX-EJJLkiSr1t77neS4Lc-KofMVmgI9a2tSSpNAagBiNI6Ak9L1T0F9uxeDfEG9bBSQPNMOSUbAoEcNxtt7xOW~cNOAyMyGydwPMnrQ5kIYPY8Pd3XudEko970vE0D6gO19yoBMJpKx6Dh50DGgybLQ9CpRaynh2zPULTHxm8rneOGRcQo8D3mE7FQ92m54~SvfjXjD2TwAVGI~ae~n9HDxt8uxOecAAvjjJ3TD4XM63Q9TmB38RmGNzNLDBQMEmJFpqQU8YeuhnS54IVdUoVQFqui5SfDeLXlSkh4vYoMU66pvBfWbAAAA.i2p/tracker/announce.php=http://crstrack.i2p/tracker/"
// ,"Exotrack", "http://blbgywsjubw3d2zih2giokakhe3o2cko7jtte4risb3hohbcoyva.b32.i2p/announce.php=http://exotrack.i2p/"
};
/** comma delimited list of name=announceURL=baseURL for the trackers to be displayed */
public static final String PROP_TRACKERS = "i2psnark.trackers";
private static Map<String, String> trackerMap = null;
/** sorted map of name to announceURL=baseURL */
public Map<String, String> getTrackers() { public Map<String, String> getTrackers() {
if (trackerMap != null) // only do this once, can't be updated while running return _trackerMap;
return trackerMap; }
Map<String, String> rv = new TreeMap();
/** @since 0.9 */
private void initTrackerMap() {
String trackers = _config.getProperty(PROP_TRACKERS); String trackers = _config.getProperty(PROP_TRACKERS);
if ( (trackers == null) || (trackers.trim().length() <= 0) ) if ( (trackers == null) || (trackers.trim().length() <= 0) )
trackers = _context.getProperty(PROP_TRACKERS); trackers = _context.getProperty(PROP_TRACKERS);
_trackerMap.clear();
if ( (trackers == null) || (trackers.trim().length() <= 0) ) { if ( (trackers == null) || (trackers.trim().length() <= 0) ) {
for (int i = 0; i < DEFAULT_TRACKERS.length; i += 2) for (int i = 0; i < DEFAULT_TRACKERS.length; i += 2)
rv.put(DEFAULT_TRACKERS[i], DEFAULT_TRACKERS[i+1]); _trackerMap.put(DEFAULT_TRACKERS[i], DEFAULT_TRACKERS[i+1]);
} else { } else {
StringTokenizer tok = new StringTokenizer(trackers, ","); String[] toks = trackers.split(",");
while (tok.hasMoreTokens()) { for (int i = 0; i < toks.length; i += 2) {
String pair = tok.nextToken(); String name = toks[i].trim().replace("&#44;", ",");
int split = pair.indexOf('='); String url = toks[i+1].trim().replace("&#44;", ",");
if (split <= 0)
continue;
String name = pair.substring(0, split).trim();
String url = pair.substring(split+1).trim();
if ( (name.length() > 0) && (url.length() > 0) ) if ( (name.length() > 0) && (url.length() > 0) )
rv.put(name, url); _trackerMap.put(name, url);
} }
} }
trackerMap = rv;
return trackerMap;
} }
/** @since 0.9 */
public void setDefaultTrackerMap() {
_trackerMap.clear();
for (int i = 0; i < DEFAULT_TRACKERS.length; i += 2) {
_trackerMap.put(DEFAULT_TRACKERS[i], DEFAULT_TRACKERS[i+1]);
}
if (_config.remove(PROP_TRACKERS) != null) {
saveConfig();
}
}
/** @since 0.9 */
public void saveTrackerMap() {
StringBuilder buf = new StringBuilder(2048);
boolean comma = false;
for (Map.Entry<String, String> e : _trackerMap.entrySet()) {
if (comma)
buf.append(',');
else
comma = true;
buf.append(e.getKey().replace(",", "&#44;")).append(',').append(e.getValue().replace(",", "&#44;"));
}
_config.setProperty(PROP_TRACKERS, buf.toString());
saveConfig();
}
private static class TorrentFilenameFilter implements FilenameFilter { private static class TorrentFilenameFilter implements FilenameFilter {
private static final TorrentFilenameFilter _filter = new TorrentFilenameFilter(); private static final TorrentFilenameFilter _filter = new TorrentFilenameFilter();
public static TorrentFilenameFilter instance() { return _filter; } public static TorrentFilenameFilter instance() { return _filter; }
@@ -1426,4 +1461,14 @@ public class SnarkManager implements Snark.CompleteListener {
} }
} }
} }
/**
* ignore case, current locale
* @since 0.9
*/
private static class IgnoreCaseComparator implements Comparator<String> {
public int compare(String l, String r) {
return l.toLowerCase().compareTo(r.toLowerCase());
}
}
} }

View File

@@ -101,7 +101,8 @@ public class Storage
* @param announce may be null * @param announce may be null
* @param listener may be null * @param listener may be null
*/ */
public Storage(I2PSnarkUtil util, File baseFile, String announce, StorageListener listener) public Storage(I2PSnarkUtil util, File baseFile, String announce,
boolean privateTorrent, StorageListener listener)
throws IOException throws IOException
{ {
_util = util; _util = util;
@@ -157,7 +158,7 @@ public class Storage
byte[] piece_hashes = fast_digestCreate(); byte[] piece_hashes = fast_digestCreate();
metainfo = new MetaInfo(announce, baseFile.getName(), null, files, metainfo = new MetaInfo(announce, baseFile.getName(), null, files,
lengthsList, piece_size, piece_hashes, total); lengthsList, piece_size, piece_hashes, total, privateTorrent);
} }

View File

@@ -156,7 +156,7 @@ public class TrackerClient extends I2PAppThread
primary = ""; primary = "";
} }
List tlist = _util.getOpenTrackers(); List tlist = _util.getOpenTrackers();
if (tlist != null) { if (tlist != null && !meta.isPrivate()) {
for (int i = 0; i < tlist.size(); i++) { for (int i = 0; i < tlist.size(); i++) {
String url = (String)tlist.get(i); String url = (String)tlist.get(i);
if (!isValidAnnounce(url)) { if (!isValidAnnounce(url)) {
@@ -348,7 +348,7 @@ public class TrackerClient extends I2PAppThread
} // *** end of trackers loop here } // *** end of trackers loop here
// Get peers from PEX // Get peers from PEX
if (left > 0 && coordinator.needPeers() && !stop) { if (left > 0 && coordinator.needPeers() && (!meta.isPrivate()) && !stop) {
Set<PeerID> pids = coordinator.getPEXPeers(); Set<PeerID> pids = coordinator.getPEXPeers();
if (!pids.isEmpty()) { if (!pids.isEmpty()) {
_util.debug("Got " + pids.size() + " from PEX", Snark.INFO); _util.debug("Got " + pids.size() + " from PEX", Snark.INFO);
@@ -370,7 +370,7 @@ public class TrackerClient extends I2PAppThread
// Get peers from DHT // Get peers from DHT
// FIXME this needs to be in its own thread // FIXME this needs to be in its own thread
if (_util.getDHT() != null && !stop) { if (_util.getDHT() != null && (!meta.isPrivate()) && !stop) {
int numwant; int numwant;
if (left == 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers()) if (left == 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
numwant = 1; numwant = 1;
@@ -444,22 +444,33 @@ public class TrackerClient extends I2PAppThread
long downloaded, long left, String event) long downloaded, long left, String event)
throws IOException throws IOException
{ {
// What do we send for left in magnet mode? Can we omit it? StringBuilder buf = new StringBuilder(512);
long tleft = left >= 0 ? left : 1; buf.append(tr.announce);
String s = tr.announce if (tr.announce.contains("?"))
+ "?info_hash=" + infoHash buf.append('&');
+ "&peer_id=" + peerID
+ "&port=" + port
+ "&ip=" + _util.getOurIPString() + ".i2p"
+ "&uploaded=" + uploaded
+ "&downloaded=" + downloaded
+ "&left=" + tleft
+ "&compact=1" // NOTE: opentracker will return 400 for &compact alone
+ ((! event.equals(NO_EVENT)) ? ("&event=" + event) : "");
if (left == 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
s += "&numwant=0";
else else
s += "&numwant=" + _util.getMaxConnections(); buf.append('?');
buf.append("info_hash=").append(infoHash)
.append("&peer_id=").append(peerID)
.append("&port=").append(port)
.append("&ip=" ).append(_util.getOurIPString()).append(".i2p")
.append("&uploaded=").append(uploaded)
.append("&downloaded=").append(downloaded)
.append("&left=");
// What do we send for left in magnet mode? Can we omit it?
if (left >= 0)
buf.append(left);
else
buf.append('1');
buf.append("&compact=1"); // NOTE: opentracker will return 400 for &compact alone
if (! event.equals(NO_EVENT))
buf.append("&event=").append(event);
buf.append("&numwant=");
if (left == 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
buf.append('0');
else
buf.append(_util.getMaxConnections());
String s = buf.toString();
_util.debug("Sending TrackerClient request: " + s, Snark.INFO); _util.debug("Sending TrackerClient request: " + s, Snark.INFO);
tr.lastRequestTime = System.currentTimeMillis(); tr.lastRequestTime = System.currentTimeMillis();

View File

@@ -102,7 +102,8 @@ public class I2PSnarkServlet extends Default {
protected Resource getResource(String pathInContext) throws IOException protected Resource getResource(String pathInContext) throws IOException
{ {
if (pathInContext == null || pathInContext.equals("/") || pathInContext.equals("/index.jsp") || if (pathInContext == null || pathInContext.equals("/") || pathInContext.equals("/index.jsp") ||
pathInContext.equals("/index.html") || pathInContext.startsWith("/_icons/")) pathInContext.equals("/index.html") || pathInContext.startsWith("/.icons/") ||
pathInContext.startsWith("/.js/") || pathInContext.startsWith("/.ajax/"))
return super.getResource(pathInContext); return super.getResource(pathInContext);
// files in the i2psnark/ directory // files in the i2psnark/ directory
return _resourceBase.addPath(pathInContext); return _resourceBase.addPath(pathInContext);
@@ -151,6 +152,19 @@ public class I2PSnarkServlet extends Default {
_imgPath = _themePath + "images/"; _imgPath = _themePath + "images/";
// this is the part after /i2psnark // this is the part after /i2psnark
String path = req.getServletPath(); String path = req.getServletPath();
// AJAX for mainsection
if ("/.ajax/xhr1.html".equals(path)) {
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html; charset=UTF-8");
PrintWriter out = resp.getWriter();
//if (_log.shouldLog(Log.DEBUG))
// _manager.addMessage((_context.clock().now() / 1000) + " xhr1 p=" + req.getParameter("p"));
writeMessages(out);
writeTorrents(out, req);
return;
}
boolean isConfigure = "/configure".equals(path); boolean isConfigure = "/configure".equals(path);
// index.jsp doesn't work, it is grabbed by the war handler before here // index.jsp doesn't work, it is grabbed by the war handler before here
if (!(path == null || path.equals("/") || path.equals("/index.jsp") || path.equals("/index.html") || path.equals("/_post") || isConfigure)) { if (!(path == null || path.equals("/") || path.equals("/index.jsp") || path.equals("/index.html") || path.equals("/_post") || isConfigure)) {
@@ -192,7 +206,8 @@ public class I2PSnarkServlet extends Default {
String peerParam = req.getParameter("p"); String peerParam = req.getParameter("p");
String peerString; String peerString;
if (peerParam == null || !_manager.util().connected()) { if (peerParam == null || (!_manager.util().connected()) ||
peerParam.replaceAll("[a-zA-Z0-9~=-]", "").length() > 0) { // XSS
peerString = ""; peerString = "";
} else { } else {
peerString = "?p=" + peerParam; peerString = "?p=" + peerParam;
@@ -208,13 +223,23 @@ public class I2PSnarkServlet extends Default {
out.write("</title>\n"); out.write("</title>\n");
// we want it to go to the base URI so we don't refresh with some funky action= value // we want it to go to the base URI so we don't refresh with some funky action= value
int delay = 0;
if (!isConfigure) { if (!isConfigure) {
int delay = _manager.getRefreshDelaySeconds(); delay = _manager.getRefreshDelaySeconds();
if (delay > 0) if (delay > 0) {
out.write("<meta http-equiv=\"refresh\" content=\"" + delay + ";/i2psnark/" + peerString + "\">\n"); //out.write("<meta http-equiv=\"refresh\" content=\"" + delay + ";/i2psnark/" + peerString + "\">\n");
out.write("<script src=\"/js/ajax.js\" type=\"text/javascript\"></script>\n" +
"<script type=\"text/javascript\">\n" +
"function requestAjax1() { ajax(\"/i2psnark/.ajax/xhr1.html" + peerString + "\", \"mainsection\", " + (delay*1000) + "); }\n" +
"function initAjax(delayMs) { setTimeout(requestAjax1, " + (delay*1000) +"); }\n" +
"</script>\n");
}
} }
out.write(HEADER_A + _themePath + HEADER_B); out.write(HEADER_A + _themePath + HEADER_B + "</head>\n");
out.write("</head><body>"); if (isConfigure || delay <= 0)
out.write("<body>");
else
out.write("<body onload=\"initAjax()\">");
out.write("<center>"); out.write("<center>");
if (isConfigure) { if (isConfigure) {
out.write("<div class=\"snarknavbar\"><a href=\"/i2psnark/\" title=\""); out.write("<div class=\"snarknavbar\"><a href=\"/i2psnark/\" title=\"");
@@ -249,25 +274,36 @@ public class I2PSnarkServlet extends Default {
String newURL = req.getParameter("newURL"); String newURL = req.getParameter("newURL");
if (newURL != null && newURL.trim().length() > 0 && req.getMethod().equals("GET")) if (newURL != null && newURL.trim().length() > 0 && req.getMethod().equals("GET"))
_manager.addMessage(_("Click \"Add torrent\" button to fetch torrent")); _manager.addMessage(_("Click \"Add torrent\" button to fetch torrent"));
out.write("<div class=\"page\"><div class=\"mainsection\"><div class=\"snarkMessages\"><table><tr><td align=\"left\"><pre>"); out.write("<div class=\"page\"><div id=\"mainsection\" class=\"mainsection\">");
writeMessages(out);
if (isConfigure) {
// end of mainsection div
out.write("<div class=\"logshim\"></div></div>\n");
writeConfigForm(out, req);
writeTrackerForm(out, req);
} else {
writeTorrents(out, req);
// end of mainsection div
out.write("</div><div id=\"lowersection\">\n");
writeAddForm(out, req);
writeSeedForm(out, req);
writeConfigLink(out);
// end of lowersection div
out.write("</div>\n");
}
out.write(FOOTER);
}
private void writeMessages(PrintWriter out) throws IOException {
out.write("<div class=\"snarkMessages\"><table><tr><td align=\"left\"><pre>");
List msgs = _manager.getMessages(); List msgs = _manager.getMessages();
for (int i = msgs.size()-1; i >= 0; i--) { for (int i = msgs.size()-1; i >= 0; i--) {
String msg = (String)msgs.get(i); String msg = (String)msgs.get(i);
out.write(msg + "\n"); out.write(msg + "\n");
} }
out.write("</pre></td></tr></table></div>"); out.write("</pre></td></tr></table></div>");
if (isConfigure) {
out.write("<div class=\"logshim\"></div></div>\n");
writeConfigForm(out, req);
} else {
writeTorrents(out, req);
out.write("</div>\n");
writeAddForm(out, req);
writeSeedForm(out, req);
writeConfigLink(out);
}
out.write(FOOTER);
} }
private void writeTorrents(PrintWriter out, HttpServletRequest req) throws IOException { private void writeTorrents(PrintWriter out, HttpServletRequest req) throws IOException {
@@ -276,7 +312,6 @@ public class I2PSnarkServlet extends Default {
String peerParam = req.getParameter("p"); String peerParam = req.getParameter("p");
List snarks = getSortedSnarks(req); List snarks = getSortedSnarks(req);
String uri = req.getRequestURI();
boolean isForm = _manager.util().connected() || !snarks.isEmpty(); boolean isForm = _manager.util().connected() || !snarks.isEmpty();
if (isForm) { if (isForm) {
out.write("<form action=\"_post\" method=\"POST\">\n"); out.write("<form action=\"_post\" method=\"POST\">\n");
@@ -390,6 +425,7 @@ public class I2PSnarkServlet extends Default {
out.write("&nbsp;"); out.write("&nbsp;");
} }
out.write("</th></tr></thead>\n"); out.write("</th></tr></thead>\n");
String uri = "/i2psnark/";
for (int i = 0; i < snarks.size(); i++) { for (int i = 0; i < snarks.size(); i++) {
Snark snark = (Snark)snarks.get(i); Snark snark = (Snark)snarks.get(i);
boolean showDebug = "2".equals(peerParam); boolean showDebug = "2".equals(peerParam);
@@ -650,14 +686,19 @@ public class I2PSnarkServlet extends Default {
_manager.updateConfig(dataDir, filesPublic, autoStart, refreshDel, startupDel, _manager.updateConfig(dataDir, filesPublic, autoStart, refreshDel, startupDel,
seedPct, eepHost, eepPort, i2cpHost, i2cpPort, i2cpOpts, seedPct, eepHost, eepPort, i2cpHost, i2cpPort, i2cpOpts,
upLimit, upBW, useOpenTrackers, openTrackers, theme); upLimit, upBW, useOpenTrackers, openTrackers, theme);
} else if ("Save2".equals(action)) {
String taction = req.getParameter("taction");
if (taction != null)
processTrackerForm(taction, req);
} 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(_manager.getDataDir(), baseData);
String announceURL = req.getParameter("announceURL"); String announceURL = req.getParameter("announceURL");
String announceURLOther = req.getParameter("announceURLOther"); // make the user add a tracker on the config form now
if ( (announceURLOther != null) && (announceURLOther.trim().length() > "http://.i2p/announce".length()) ) //String announceURLOther = req.getParameter("announceURLOther");
announceURL = announceURLOther; //if ( (announceURLOther != null) && (announceURLOther.trim().length() > "http://.i2p/announce".length()) )
// announceURL = announceURLOther;
if (announceURL == null || announceURL.length() <= 0) if (announceURL == null || announceURL.length() <= 0)
_manager.addMessage(_("Error creating torrent - you must select a tracker")); _manager.addMessage(_("Error creating torrent - you must select a tracker"));
@@ -668,7 +709,7 @@ public class I2PSnarkServlet extends Default {
try { try {
// This may take a long time to check the storage, but since it already exists, // This may take a long time to check the storage, but since it already exists,
// it shouldn't be THAT bad, so keep it in this thread. // it shouldn't be THAT bad, so keep it in this thread.
Storage s = new Storage(_manager.util(), baseFile, announceURL, null); Storage s = new Storage(_manager.util(), baseFile, announceURL, req.getParameter("private") != null, null);
s.close(); // close the files... maybe need a way to pass this Storage to addTorrent rather than starting over s.close(); // close the files... maybe need a way to pass this Storage to addTorrent rather than starting over
MetaInfo info = s.getMetaInfo(); MetaInfo info = s.getMetaInfo();
File torrentFile = new File(_manager.getDataDir(), s.getBaseName() + ".torrent"); File torrentFile = new File(_manager.getDataDir(), s.getBaseName() + ".torrent");
@@ -713,6 +754,54 @@ public class I2PSnarkServlet extends Default {
_manager.addMessage("Unknown POST action: \"" + action + '\"'); _manager.addMessage("Unknown POST action: \"" + action + '\"');
} }
} }
/** @since 0.9 */
private void processTrackerForm(String action, HttpServletRequest req) {
if (action.equals(_("Delete selected"))) {
boolean changed = false;
Map<String, String> trackers = _manager.getTrackers();
Enumeration e = req.getParameterNames();
while (e.hasMoreElements()) {
Object o = e.nextElement();
if (!(o instanceof String))
continue;
String k = (String) o;
if (!k.startsWith("delete_"))
continue;
k = k.substring(7);
if (trackers.remove(k) != null) {
_manager.addMessage(_("Removed") + ": " + k);
changed = true;
}
}
if (changed) {
_manager.saveTrackerMap();
}
} else if (action.equals(_("Add tracker"))) {
String name = req.getParameter("tname");
String hurl = req.getParameter("thurl");
String aurl = req.getParameter("taurl");
if (name != null && hurl != null && aurl != null) {
name = name.trim();
hurl = hurl.trim();
aurl = aurl.trim().replace("=", "&#61;");
if (name.length() > 0 && hurl.startsWith("http://") && aurl.startsWith("http://")) {
Map<String, String> trackers = _manager.getTrackers();
trackers.put(name, aurl + '=' + hurl);
_manager.saveTrackerMap();
} else {
_manager.addMessage(_("Enter valid tracker name and URLs"));
}
} else {
_manager.addMessage(_("Enter valid tracker name and URLs"));
}
} else if (action.equals(_("Restore defaults"))) {
_manager.setDefaultTrackerMap();
_manager.addMessage(_("Restored default trackers"));
} else {
_manager.addMessage("Unknown POST action: \"" + action + '\"');
}
}
private static final String iopts[] = {"inbound.length", "inbound.quantity", private static final String iopts[] = {"inbound.length", "inbound.quantity",
"outbound.length", "outbound.quantity" }; "outbound.length", "outbound.quantity" };
@@ -1222,7 +1311,7 @@ public class I2PSnarkServlet extends Default {
out.write("\"> \n"); out.write("\"> \n");
// not supporting from file at the moment, since the file name passed isn't always absolute (so it may not resolve) // not supporting from file at the moment, since the file name passed isn't always absolute (so it may not resolve)
//out.write("From file: <input type=\"file\" name=\"newFile\" size=\"50\" value=\"" + newFile + "\" /><br>"); //out.write("From file: <input type=\"file\" name=\"newFile\" size=\"50\" value=\"" + newFile + "\" /><br>");
out.write("<input type=\"submit\" value=\""); out.write("<input type=\"submit\" class=\"add\" value=\"");
out.write(_("Add torrent")); out.write(_("Add torrent"));
out.write("\" name=\"foo\" ><br>\n"); out.write("\" name=\"foo\" ><br>\n");
out.write("<tr><td>&nbsp;<td><span class=\"snarkAddInfo\">"); out.write("<tr><td>&nbsp;<td><span class=\"snarkAddInfo\">");
@@ -1269,31 +1358,40 @@ public class I2PSnarkServlet extends Default {
//out.write(_("Open trackers and DHT only")); //out.write(_("Open trackers and DHT only"));
out.write(_("Open trackers only")); out.write(_("Open trackers only"));
out.write("</option>\n"); out.write("</option>\n");
Map trackers = _manager.getTrackers(); Map<String, String> trackers = _manager.getTrackers();
for (Iterator iter = trackers.entrySet().iterator(); iter.hasNext(); ) { for (Map.Entry<String, String> entry : trackers.entrySet()) {
Map.Entry entry = (Map.Entry)iter.next(); String name = entry.getKey();
String name = (String)entry.getKey(); String announceURL = entry.getValue();
String announceURL = (String)entry.getValue();
int e = announceURL.indexOf('='); int e = announceURL.indexOf('=');
if (e > 0) if (e > 0)
announceURL = announceURL.substring(0, e); announceURL = announceURL.substring(0, e).replace("&#61;", "=");
if (announceURL.equals(_lastAnnounceURL)) if (announceURL.equals(_lastAnnounceURL))
announceURL += "\" selected=\"selected"; announceURL += "\" selected=\"selected";
out.write("\t<option value=\"" + announceURL + "\">" + name + "</option>\n"); out.write("\t<option value=\"" + announceURL + "\">" + name + "</option>\n");
} }
out.write("</select>\n"); out.write("</select>\n");
out.write(_("or")); // make the user add a tracker on the config form now
out.write("&nbsp;<input type=\"text\" name=\"announceURLOther\" size=\"57\" value=\"http://\" " + //out.write(_("or"));
"title=\""); //out.write("&nbsp;<input type=\"text\" name=\"announceURLOther\" size=\"57\" value=\"http://\" " +
out.write(_("Specify custom tracker announce URL")); // "title=\"");
out.write("\" > " + //out.write(_("Specify custom tracker announce URL"));
"<input type=\"submit\" value=\""); //out.write("\" > " +
out.write(" <input type=\"submit\" class=\"create\" value=\"");
out.write(_("Create torrent")); out.write(_("Create torrent"));
out.write("\" name=\"foo\" ></table>\n" + out.write("\" name=\"foo\" >\n" +
"</td></tr><tr><td>");
out.write(_("Private?"));
out.write(" </td><td> <input type=\"checkbox\" class=\"optbox\" name=\"private\" value=\"true\" title=\"");
out.write(_("Use for private trackers"));
out.write("\"");
if (req.getParameter("private") != null)
out.write(" checked");
out.write("></td></tr>" +
"</table>\n" +
"</form></div></div>"); "</form></div></div>");
} }
private static final int[] times = { 30, 60, 2*60, 5*60, 10*60, 30*60, -1 }; private static final int[] times = { 5, 15, 30, 60, 2*60, 5*60, 10*60, 30*60, -1 };
private void writeConfigForm(PrintWriter out, HttpServletRequest req) throws IOException { private void writeConfigForm(PrintWriter out, HttpServletRequest req) throws IOException {
String dataDir = _manager.getDataDir().getAbsolutePath(); String dataDir = _manager.getDataDir().getAbsolutePath();
@@ -1356,7 +1454,7 @@ public class I2PSnarkServlet extends Default {
out.write(Integer.toString(times[i])); out.write(Integer.toString(times[i]));
out.write("\""); out.write("\"");
if (times[i] == delay) if (times[i] == delay)
out.write(" selected=\"true\""); out.write(" selected=\"selected\"");
out.write(">"); out.write(">");
if (times[i] > 0) if (times[i] > 0)
out.write(DataHelper.formatDuration2(times[i] * 1000)); out.write(DataHelper.formatDuration2(times[i] * 1000));
@@ -1379,15 +1477,15 @@ public class I2PSnarkServlet extends Default {
/* /*
out.write("Seed percentage: <select name=\"seedPct\" disabled=\"true\" >\n\t"); out.write("Seed percentage: <select name=\"seedPct\" disabled=\"true\" >\n\t");
if (seedPct <= 0) if (seedPct <= 0)
out.write("<option value=\"0\" selected=\"true\">Unlimited</option>\n\t"); out.write("<option value=\"0\" selected=\"selected\">Unlimited</option>\n\t");
else else
out.write("<option value=\"0\">Unlimited</option>\n\t"); out.write("<option value=\"0\">Unlimited</option>\n\t");
if (seedPct == 100) if (seedPct == 100)
out.write("<option value=\"100\" selected=\"true\">100%</option>\n\t"); out.write("<option value=\"100\" selected=\"selected\">100%</option>\n\t");
else else
out.write("<option value=\"100\">100%</option>\n\t"); out.write("<option value=\"100\">100%</option>\n\t");
if (seedPct == 150) if (seedPct == 150)
out.write("<option value=\"150\" selected=\"true\">150%</option>\n\t"); out.write("<option value=\"150\" selected=\"selected\">150%</option>\n\t");
else else
out.write("<option value=\"150\">150%</option>\n\t"); out.write("<option value=\"150\">150%</option>\n\t");
out.write("</select><br>\n"); out.write("</select><br>\n");
@@ -1473,6 +1571,57 @@ public class I2PSnarkServlet extends Default {
"</table></div></div></form>"); "</table></div></div></form>");
} }
/** @since 0.9 */
private void writeTrackerForm(PrintWriter out, HttpServletRequest req) throws IOException {
StringBuilder buf = new StringBuilder(1024);
buf.append("<form action=\"/i2psnark/configure\" method=\"POST\">\n" +
"<div class=\"configsectionpanel\"><div class=\"snarkConfig\">\n" +
"<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" >\n" +
"<input type=\"hidden\" name=\"action\" value=\"Save2\" >\n" +
"<span class=\"snarkConfigTitle\">" +
"<img alt=\"\" border=\"0\" src=\"" + _imgPath + "config.png\"> ");
buf.append(_("Trackers"));
buf.append("</span><hr>\n" +
"<table><tr><th>")
//.append(_("Remove"))
.append("</th><th>")
.append(_("Name"))
.append("</th><th>")
.append(_("Website URL"))
.append("</th><th>")
.append(_("Announce URL"))
.append("</th></tr>\n");
Map<String, String> trackers = _manager.getTrackers();
for (Map.Entry<String, String> entry : trackers.entrySet()) {
String name = entry.getKey();
String announceURL = entry.getValue();
int e = announceURL.indexOf('=');
if (e <= 0)
continue;
String homeURL = announceURL.substring(e + 1);
announceURL = announceURL.substring(0, e).replace("&#61;", "=");
buf.append("<tr><td align=\"center\"><input type=\"checkbox\" class=\"optbox\" name=\"delete_")
.append(name).append("\">" +
"</td><td align=\"left\">").append(name)
.append("</td><td align=\"left\">").append(urlify(homeURL, 35))
.append("</td><td align=\"left\">").append(urlify(announceURL, 35))
.append("</td></tr>\n");
}
buf.append("<tr><td align=\"center\"><b>")
.append(_("Add")).append(":</b></td>" +
"<td align=\"left\"><input type=\"text\" size=\"16\" name=\"tname\"></td>" +
"<td align=\"left\"><input type=\"text\" size=\"40\" name=\"thurl\"></td>" +
"<td align=\"left\"><input type=\"text\" size=\"40\" name=\"taurl\"></td></tr>\n" +
"<tr><td colspan=\"2\"></td><td colspan=\"2\" align=\"left\">\n" +
"<input type=\"submit\" name=\"taction\" class=\"default\" value=\"").append(_("Add tracker")).append("\">\n" +
"<input type=\"submit\" name=\"taction\" class=\"delete\" value=\"").append(_("Delete selected")).append("\">\n" +
// "<input type=\"reset\" class=\"cancel\" value=\"").append(_("Cancel")).append("\">\n" +
"<input type=\"submit\" name=\"taction\" class=\"reload\" value=\"").append(_("Restore defaults")).append("\">\n" +
"<input type=\"submit\" name=\"taction\" class=\"add\" value=\"").append(_("Add tracker")).append("\">\n" +
"</td></tr></table></div></div></form>\n");
out.write(buf.toString());
}
private void writeConfigLink(PrintWriter out) throws IOException { private void writeConfigLink(PrintWriter out) throws IOException {
out.write("<div class=\"configsection\"><span class=\"snarkConfig\">\n" + out.write("<div class=\"configsection\"><span class=\"snarkConfig\">\n" +
"<span class=\"snarkConfigTitle\"><a href=\"configure\">" + "<span class=\"snarkConfigTitle\"><a href=\"configure\">" +
@@ -1574,7 +1723,7 @@ public class I2PSnarkServlet extends Default {
for (int i = min; i <= max; i++) { for (int i = min; i <= max; i++) {
buf.append("<option value=\"").append(i).append("\" "); buf.append("<option value=\"").append(i).append("\" ");
if (i == now) if (i == now)
buf.append("selected=\"true\" "); buf.append("selected=\"selected\" ");
// constants to prevent tagging // constants to prevent tagging
buf.append(">").append(ngettext(DUMMY1 + name, DUMMY0 + name + 's', i)); buf.append(">").append(ngettext(DUMMY1 + name, DUMMY0 + name + 's', i));
buf.append("</option>\n"); buf.append("</option>\n");
@@ -1622,10 +1771,20 @@ public class I2PSnarkServlet extends Default {
/** @since 0.7.14 */ /** @since 0.7.14 */
private static String urlify(String s) { private static String urlify(String s) {
return urlify(s, 100);
}
/** @since 0.9 */
private static String urlify(String s, int max) {
StringBuilder buf = new StringBuilder(256); StringBuilder buf = new StringBuilder(256);
// browsers seem to work without doing this but let's be strict // browsers seem to work without doing this but let's be strict
String link = urlEncode(s); String link = urlEncode(s);
buf.append("<a href=\"").append(link).append("\">").append(link).append("</a>"); String display;
if (s.length() <= max)
display = link;
else
display = urlEncode(s.substring(0, max)) + "&hellip;";
buf.append("<a href=\"").append(link).append("\">").append(display).append("</a>");
return buf.toString(); return buf.toString();
} }
@@ -1991,12 +2150,12 @@ public class I2PSnarkServlet extends Default {
/** @since 0.7.14 */ /** @since 0.7.14 */
private static String toImg(String icon) { private static String toImg(String icon) {
return "<img alt=\"\" height=\"16\" width=\"16\" src=\"/i2psnark/_icons/" + icon + ".png\">"; return "<img alt=\"\" height=\"16\" width=\"16\" src=\"/i2psnark/.icons/" + icon + ".png\">";
} }
/** @since 0.8.2 */ /** @since 0.8.2 */
private static String toImg(String icon, String altText) { private static String toImg(String icon, String altText) {
return "<img alt=\"" + altText + "\" height=\"16\" width=\"16\" src=\"/i2psnark/_icons/" + icon + ".png\">"; return "<img alt=\"" + altText + "\" height=\"16\" width=\"16\" src=\"/i2psnark/.icons/" + icon + ".png\">";
} }
/** @since 0.8.1 */ /** @since 0.8.1 */

View File

@@ -63,6 +63,7 @@
<attribute name="Main-Class" value="net.i2p.i2ptunnel.I2PTunnel" /> <attribute name="Main-Class" value="net.i2p.i2ptunnel.I2PTunnel" />
<attribute name="Class-Path" value="i2p.jar mstreaming.jar" /> <attribute name="Class-Path" value="i2p.jar mstreaming.jar" />
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.j.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.j.tr}" />
@@ -136,6 +137,7 @@
basedir="../jsp/" excludes="web.xml, web-fragment.xml, web-out.xml, **/*.java, *.jsp"> basedir="../jsp/" excludes="web.xml, web-fragment.xml, web-out.xml, **/*.java, *.jsp">
<manifest> <manifest>
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.w.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.w.tr}" />

View File

@@ -13,6 +13,8 @@ import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
@@ -36,8 +38,8 @@ 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;
import net.i2p.i2ptunnel.localServer.LocalHTTPServer;
import net.i2p.util.EventDispatcher; import net.i2p.util.EventDispatcher;
import net.i2p.util.FileUtil;
import net.i2p.util.Log; import net.i2p.util.Log;
import net.i2p.util.PortMapper; import net.i2p.util.PortMapper;
import net.i2p.util.Translate; import net.i2p.util.Translate;
@@ -61,6 +63,10 @@ import net.i2p.util.Translate;
* in browsers or other user-visible applications, as relative links will not * in browsers or other user-visible applications, as relative links will not
* resolve correctly, cookies won't work, etc. * resolve correctly, cookies won't work, etc.
* *
* Note that http://$b64key/... and http://$b64key.i2p/... are NOT supported, as
* a b64 key may contain '=' and '~', both of which are illegal host name characters.
* Rewrite as http://i2p/$b64key/...
*
* If the $site resolves with the I2P naming service, then it is directed towards * If the $site resolves with the I2P naming service, then it is directed towards
* that eepsite, otherwise it is directed towards this client's outproxy (typically * that eepsite, otherwise it is directed towards this client's outproxy (typically
* "squid.i2p"). Only HTTP is supported (no HTTPS, ftp, mailto, etc). Both GET * "squid.i2p"). Only HTTP is supported (no HTTPS, ftp, mailto, etc). Both GET
@@ -102,7 +108,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"That I2P Destination was not found. Perhaps you pasted in the "+ "That I2P Destination was not found. Perhaps you pasted in the "+
"wrong BASE64 I2P Destination or the link you are following is "+ "wrong BASE64 I2P Destination or the link you are following is "+
"bad. The host (or the WWW proxy, if you're using one) could also "+ "bad. The host (or the WWW proxy, if you're using one) could also "+
"be temporarily offline. You may want to <b>retry</b>. "+ "be temporarily offline. You may want to <b>retry</b>. "+
"Could not find the following Destination:<BR><BR><div>") "Could not find the following Destination:<BR><BR><div>")
.getBytes(); .getBytes();
@@ -309,7 +315,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
return rv; return rv;
} }
private static final String LOCAL_SERVER = "proxy.i2p"; private static final String HELPER_PARAM = "i2paddresshelper";
public static final String LOCAL_SERVER = "proxy.i2p";
private static final boolean DEFAULT_GZIP = true; private static final boolean DEFAULT_GZIP = true;
/** all default to false */ /** all default to false */
public static final String PROP_REFERER = "i2ptunnel.httpclient.sendReferer"; public static final String PROP_REFERER = "i2ptunnel.httpclient.sendReferer";
@@ -321,11 +328,19 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
protected void clientConnectionRun(Socket s) { protected void clientConnectionRun(Socket s) {
InputStream in = null; InputStream in = null;
OutputStream out = null; OutputStream out = null;
/**
* The URL after fixup, always starting with http://
*/
String targetRequest = null; String targetRequest = null;
boolean usingWWWProxy = false; boolean usingWWWProxy = false;
boolean usingInternalServer = false; boolean usingInternalServer = false;
String internalPath = null;
String internalRawQuery = null;
String currentProxy = null; String currentProxy = null;
long requestId = ++__requestId; long requestId = ++__requestId;
try { try {
out = s.getOutputStream(); out = s.getOutputStream();
InputReader reader = new InputReader(s.getInputStream()); InputReader reader = new InputReader(s.getInputStream());
@@ -351,79 +366,84 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix(requestId) + "First line [" + line + "]"); _log.debug(getPrefix(requestId) + "First line [" + line + "]");
int pos = line.indexOf(" "); String[] params = line.split(" ", 3);
if (pos == -1) break; if (params.length != 3)
method = line.substring(0, pos); break;
// TODO use Java URL class to make all this simpler and more robust String request = params[1];
// That will also fix IPV6 [a:b:c]
String request = line.substring(pos + 1); // various obscure fixups
if (request.startsWith("/") && getTunnel().getClientOptions().getProperty("i2ptunnel.noproxy") != null) { if (request.startsWith("/") && getTunnel().getClientOptions().getProperty("i2ptunnel.noproxy") != null) {
// what is this for ??? // what is this for ???
request = "http://i2p" + request; request = "http://i2p" + request;
} else if (request.startsWith("/eepproxy/")) { } else if (request.startsWith("/eepproxy/")) {
// /eepproxy/foo.i2p/bar/baz.html HTTP/1.0 // Deprecated
// /eepproxy/foo.i2p/bar/baz.html
String subRequest = request.substring("/eepproxy/".length()); String subRequest = request.substring("/eepproxy/".length());
int protopos = subRequest.indexOf(" "); if (subRequest.indexOf("/") == -1)
String uri = subRequest.substring(0, protopos); subRequest += "/";
if (uri.indexOf("/") == -1) { request = "http://" + subRequest;
uri = uri + "/"; /****
}
// "http://" + "foo.i2p/bar/baz.html" + " HTTP/1.0"
request = "http://" + uri + subRequest.substring(protopos);
} else if (request.toLowerCase(Locale.US).startsWith("http://i2p/")) { } else if (request.toLowerCase(Locale.US).startsWith("http://i2p/")) {
// http://i2p/b64key/bar/baz.html HTTP/1.0 // http://i2p/b64key/bar/baz.html
// we can't do this now by setting the URI host to the b64key, as
// it probably contains '=' and '~' which are illegal,
// and a host may not include escaped octets
// This will get undone below.
String subRequest = request.substring("http://i2p/".length()); String subRequest = request.substring("http://i2p/".length());
int protopos = subRequest.indexOf(" "); if (subRequest.indexOf("/") == -1)
String uri = subRequest.substring(0, protopos); subRequest += "/";
if (uri.indexOf("/") == -1) { "http://" + "b64key/bar/baz.html"
uri = uri + "/"; request = "http://" + subRequest;
} } else if (request.toLowerCase(Locale.US).startsWith("http://")) {
// "http://" + "b64key/bar/baz.html" + " HTTP/1.0" // Unsupported
request = "http://" + uri + subRequest.substring(protopos); // http://$b64key/...
// This probably used to work, rewrite it so that
// we can create a URI without illegal characters
// This will get undone below.
String oldPath = request.substring(7);
int slash = oldPath.indexOf("/");
if (slash < 0)
slash = oldPath.length();
if (slash >= 516 && !oldPath.substring(0, slash).contains("."))
request = "http://i2p/" + oldPath;
****/
} }
pos = request.indexOf("//"); // Now use the Java URI parser
if (pos == -1) { // This will be the incoming URI but will then get modified
// to be the outgoing URI (with http:// if going to outproxy, otherwise without)
URI requestURI;
try {
requestURI = new URI(request);
if (requestURI.getRawUserInfo() != null || requestURI.getRawFragment() != null) {
// these should never be sent to the proxy in the request line
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix(requestId) + "Removing userinfo or fragment [" + request + "]");
requestURI = changeURI(requestURI, null, 0, null);
}
if (requestURI.getPath() == null || requestURI.getPath().length() <= 0) {
// Add a path
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix(requestId) + "Adding / path to [" + request + "]");
requestURI = changeURI(requestURI, null, 0, "/");
}
} catch (URISyntaxException use) {
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix(requestId) + "Bad request [" + request + "]", use);
break;
}
method = params[0];
String protocolVersion = params[2];
protocol = requestURI.getScheme();
host = requestURI.getHost();
if (protocol == null || host == null) {
_log.warn("Null protocol or host: " + request);
method = null; method = null;
break; break;
} }
protocol = request.substring(0, pos + 2);
request = request.substring(pos + 2);
// "foo.i2p/bar/baz HTTP/1.1", with any i2paddresshelper parameter removed int port = requestURI.getPort();
targetRequest = request;
// pos is the start of the path
pos = request.indexOf("/");
if (pos == -1) {
//pos = request.length();
method = null;
break;
}
host = request.substring(0, pos);
// parse port
int posPort = host.indexOf(":");
int port = 80;
if(posPort != -1) {
String[] parts = host.split(":");
try {
host = parts[0];
} catch (ArrayIndexOutOfBoundsException ex) {
if (out != null) {
out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
writeFooter(out);
}
s.close();
return;
}
try {
port = Integer.parseInt(parts[1]);
} catch(Exception exc) {
// TODO: log this
}
}
// Go through the various types of host names, set // Go through the various types of host names, set
// the host and destination variables accordingly, // the host and destination variables accordingly,
@@ -433,115 +453,142 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
// in our addressbook (all naming is local), // in our addressbook (all naming is local),
// and it is removed from the request line. // and it is removed from the request line.
if (host.length() >= 516 && host.indexOf(".") < 0) { String hostLowerCase = host.toLowerCase(Locale.US);
// http://b64key/bar/baz.html if (hostLowerCase.equals(LOCAL_SERVER)) {
destination = host;
host = getHostName(destination);
line = method + ' ' + request.substring(pos);
} else if (host.toLowerCase(Locale.US).equals(LOCAL_SERVER)) {
// so we don't do any naming service lookups // so we don't do any naming service lookups
destination = host; destination = host;
usingInternalServer = true; usingInternalServer = true;
} else if (host.toLowerCase(Locale.US).endsWith(".i2p")) { internalPath = requestURI.getPath();
internalRawQuery = requestURI.getRawQuery();
} else if (hostLowerCase.equals("i2p")) {
// pull the b64 dest out of the first path element
String oldPath = requestURI.getPath().substring(1);
int slash = oldPath.indexOf("/");
if (slash < 0) {
slash = oldPath.length();
oldPath += "/";
}
String dest = oldPath.substring(0, slash);
if (slash >= 516 && !dest.contains(".")) {
// possible alternative:
// redirect to b32
destination = dest;
host = getHostName(destination);
targetRequest = requestURI.toASCIIString();
String newURI = oldPath.substring(slash);
String query = requestURI.getRawQuery();
if (query != null)
newURI += '?' + query;
try {
requestURI = new URI(newURI);
} catch (URISyntaxException use) {
// shouldnt happen
_log.warn(request, use);
method = null;
break;
}
} else {
_log.warn("Bad http://i2p/b64dest " + request);
host = null;
break;
}
} else if (hostLowerCase.endsWith(".i2p")) {
// Destination gets the host name // Destination gets the host name
destination = host; destination = host;
// Host becomes the destination's "{b32}.b32.i2p" string, or "i2p" on lookup failure // Host becomes the destination's "{b32}.b32.i2p" string, or "i2p" on lookup failure
host = getHostName(destination); host = getHostName(destination);
int pos2; if (requestURI.getPort() >= 0) {
if ((pos2 = request.indexOf("?")) != -1) { // TODO support I2P ports someday
// Try to find an address helper in the fragments //if (port >= 0)
// and split the request into it's component parts for rebuilding later // host = host + ':' + port;
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix(requestId) + "Removing port from [" + request + "]");
try {
requestURI = changeURI(requestURI, null, -1, null);
} catch (URISyntaxException use) {
_log.warn(request, use);
method = null;
break;
}
}
String query = requestURI.getRawQuery();
if (query != null) {
boolean ahelperConflict = false; boolean ahelperConflict = false;
String fragments = request.substring(pos2 + 1); // Try to find an address helper in the query
String uriPath = request.substring(0, pos2); String[] helperStrings = removeHelper(query);
pos2 = fragments.indexOf(" "); if (helperStrings != null &&
String protocolVersion = fragments.substring(pos2 + 1); !Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
String urlEncoding = ""; query = helperStrings[0];
fragments = fragments.substring(0, pos2); if (query.equals(""))
String initialFragments = fragments; query = null;
// FIXME split on ';' also try {
fragments = fragments + "&"; requestURI = replaceQuery(requestURI, query);
String fragment; } catch (URISyntaxException use) {
while(fragments.length() > 0) { // shouldn't happen
pos2 = fragments.indexOf("&"); _log.warn(request, use);
fragment = fragments.substring(0, pos2); method = null;
fragments = fragments.substring(pos2 + 1); break;
}
ahelperKey = helperStrings[1];
// Key contains data, lets not ignore it
if (ahelperKey.length() > 0) {
if(ahelperKey.endsWith(".i2p")) {
// allow i2paddresshelper=<b32>.b32.i2p syntax.
/*
also i2paddresshelper=name.i2p for aliases
i.e. on your eepsite put
<a href="?i2paddresshelper=name.i2p">This is the name I want to be called.</a>
*/
Destination dest = _context.namingService().lookup(ahelperKey);
if(dest==null) {
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix(requestId) + "Could not find destination for "+ahelperKey);
byte[] header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
out.write(header);
out.write(("<p>" + _("This seems to be a bad destination:") + " " + ahelperKey + " " + _("i2paddresshelper cannot help you with a destination like that!") + "</p>").getBytes("UTF-8"));
writeFooter(out);
// XXX: should closeSocket(s) be in a finally block?
closeSocket(s);
return;
}
ahelperKey = dest.toBase64();
}
// Fragment looks like addresshelper key ahelperPresent = true;
if (fragment.startsWith("i2paddresshelper=") && // ahelperKey will be validated later
!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) { if (host == null || "i2p".equals(host)) {
pos2 = fragment.indexOf("="); // Host lookup failed - resolvable only with addresshelper
ahelperKey = fragment.substring(pos2 + 1); // Store in local HashMap unless there is conflict
// Key contains data, lets not ignore it String old = addressHelpers.putIfAbsent(destination.toLowerCase(Locale.US), ahelperKey);
if (ahelperKey != null) { ahelperNew = old == null;
if(ahelperKey.endsWith(".i2p")) { if ((!ahelperNew) && !old.equals(ahelperKey)) {
// allow i2paddresshelper=<b32>.b32.i2p syntax. // Conflict: handle when URL reconstruction done
/* ahelperConflict = true;
also i2paddresshelper=name.i2p for aliases if (_log.shouldLog(Log.WARN))
i.e. on your eepsite put _log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination +
<a href="?i2paddresshelper=name.i2p">This is the name I want to be called.</a> "], trusted key [" + old + "], specified key [" + ahelperKey + "].");
*/ }
Destination dest = _context.namingService().lookup(ahelperKey); } else {
if(dest==null) { // If the host is resolvable from database, verify addresshelper key
if (_log.shouldLog(Log.WARN)) // Silently bypass correct keys, otherwise alert
_log.warn(getPrefix(requestId) + "Could not find destination for "+ahelperKey); Destination hostDest = _context.namingService().lookup(destination);
byte[] header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND); if (hostDest != null) {
out.write(header); String destB64 = hostDest.toBase64();
out.write(("<p>" + _("This seems to be a bad destination:") + " " + ahelperKey + " " + _("i2paddresshelper cannot help you with a destination like that!") + "</p>").getBytes("UTF-8")); if (destB64 != null && !destB64.equals(ahelperKey)) {
writeFooter(out);
// XXX: should closeSocket(s) be in a finally block?
closeSocket(s);
return;
}
ahelperKey = dest.toBase64();
}
ahelperPresent = true;
// ahelperKey will be validated later
if (host == null || "i2p".equals(host)) {
// Host lookup failed - resolvable only with addresshelper
// Store in local HashMap unless there is conflict
String old = addressHelpers.putIfAbsent(destination.toLowerCase(Locale.US), ahelperKey);
ahelperNew = old == null;
if ((!ahelperNew) && !old.equals(ahelperKey)) {
// Conflict: handle when URL reconstruction done // Conflict: handle when URL reconstruction done
ahelperConflict = true; ahelperConflict = true;
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination + _log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination +
"], trusted key [" + old + "], specified key [" + ahelperKey + "]."); "], trusted key [" + destB64 + "], specified key [" + ahelperKey + "].");
}
} else {
// If the host is resolvable from database, verify addresshelper key
// Silently bypass correct keys, otherwise alert
Destination hostDest = _context.namingService().lookup(destination);
if (hostDest != null) {
String destB64 = hostDest.toBase64();
if (destB64 != null && !destB64.equals(ahelperKey)) {
// Conflict: handle when URL reconstruction done
ahelperConflict = true;
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination +
"], trusted key [" + destB64 + "], specified key [" + ahelperKey + "].");
}
} }
} }
} // ahelperKey
} else {
// Other fragments, just pass along
// Append each fragment to urlEncoding
if ("".equals(urlEncoding)) {
urlEncoding = "?" + fragment;
} else {
urlEncoding = urlEncoding + "&" + fragment;
} }
} } // ahelperKey
} } // helperstrings
// Reconstruct the request minus the i2paddresshelper GET var
request = uriPath + urlEncoding + " " + protocolVersion;
targetRequest = request;
// Did addresshelper key conflict? // Did addresshelper key conflict?
if (ahelperConflict) { if (ahelperConflict) {
@@ -553,9 +600,17 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
byte[] header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN); byte[] header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
writeErrorMessage(header, out, targetRequest, false, destination, null); writeErrorMessage(header, out, targetRequest, false, destination, null);
} else { } else {
String trustedURL = protocol + uriPath + urlEncoding; String trustedURL = requestURI.toASCIIString();
// Fixme - any path is lost URI conflictURI;
String conflictURL = protocol + alias + '/' + urlEncoding; try {
conflictURI = changeURI(requestURI, alias, 0, null);
} catch (URISyntaxException use) {
// shouldn't happen
_log.warn(request, use);
method = null;
break;
}
String conflictURL = conflictURI.toASCIIString();
byte[] header = getErrorPage("ahelper-conflict", ERR_AHELPER_CONFLICT); byte[] header = getErrorPage("ahelper-conflict", ERR_AHELPER_CONFLICT);
out.write(header); out.write(header);
out.write(_("To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper destination, click <a href=\"{1}\">here</a>.", trustedURL, conflictURL).getBytes("UTF-8")); out.write(_("To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper destination, click <a href=\"{1}\">here</a>.", trustedURL, conflictURL).getBytes("UTF-8"));
@@ -572,11 +627,24 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if (addressHelper != null) if (addressHelper != null)
host = getHostName(addressHelper); host = getHostName(addressHelper);
line = method + " " + request.substring(pos); // now strip everything but path and query from URI
targetRequest = requestURI.toASCIIString();
String newURI = requestURI.getRawPath();
if (query != null)
newURI += '?' + query;
try {
requestURI = new URI(newURI);
} catch (URISyntaxException use) {
// shouldnt happen
_log.warn(request, use);
method = null;
break;
}
// end of (host endsWith(".i2p")) // end of (host endsWith(".i2p"))
} else if (host.toLowerCase(Locale.US).equals("localhost") || host.equals("127.0.0.1") || } else if (hostLowerCase.equals("localhost") || host.equals("127.0.0.1") ||
host.startsWith("192.168.")) { host.startsWith("192.168.") || host.equals("[::1]")) {
// if somebody is trying to get to 192.168.example.com, oh well // if somebody is trying to get to 192.168.example.com, oh well
if (out != null) { if (out != null) {
out.write(getErrorPage("localhost", ERR_LOCALHOST)); out.write(getErrorPage("localhost", ERR_LOCALHOST));
@@ -584,9 +652,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
} }
s.close(); s.close();
return; return;
} else if (host.indexOf(".") != -1) { } else if (host.contains(".") || host.startsWith("[")) {
// rebuild host if (port >= 0)
host = host + ":" + port; host = host + ':' + port;
// The request must be forwarded to a WWW proxy // The request must be forwarded to a WWW proxy
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Before selecting outproxy for " + host); _log.debug("Before selecting outproxy for " + host);
@@ -606,36 +674,23 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
} }
destination = currentProxy; destination = currentProxy;
usingWWWProxy = true; usingWWWProxy = true;
targetRequest = requestURI.toASCIIString();
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix(requestId) + "Host doesnt end with .i2p and it contains a period [" + host + "]: wwwProxy!"); _log.debug(getPrefix(requestId) + " [" + host + "]: wwwProxy!");
} else { } else {
// what is left for here? a hostname with no dots, and != "i2p" // what is left for here? a hostname with no dots, and != "i2p"
// and not a destination ??? // and not a destination ???
// Perhaps something in privatehosts.txt ... // Perhaps something in privatehosts.txt ...
request = request.substring(pos + 1); // Rather than look it up, just bail out.
pos = request.indexOf("/"); if (_log.shouldLog(Log.WARN))
if (pos < 0) { _log.warn("NODOTS, NOI2P: " + request);
l.log("Invalid request url [" + request + "]");
if (out != null) {
out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
writeFooter(out);
}
s.close();
return;
}
destination = request.substring(0, pos);
host = getHostName(destination);
line = method + " " + request.substring(pos);
} // end host name processing
if (port != 80 && !usingWWWProxy) {
if (out != null) { if (out != null) {
out.write(getErrorPage("denied", ERR_REQUEST_DENIED)); out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
writeFooter(out); writeFooter(out);
} }
s.close(); s.close();
return; return;
} } // end host name processing
boolean isValid = usingWWWProxy || usingInternalServer || isSupportedAddress(host, protocol); boolean isValid = usingWWWProxy || usingInternalServer || isSupportedAddress(host, protocol);
if (!isValid) { if (!isValid) {
@@ -645,18 +700,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
break; break;
} }
// don't do this, it forces yet another hostname lookup, line = method + ' ' + requestURI.toASCIIString() + ' ' + protocolVersion;
// and in all cases host was already set above
//if ((!usingWWWProxy) && (!usingInternalServer)) {
// String oldhost = host;
// host = getHostName(destination); // hide original host
// if (_log.shouldLog(Log.INFO))
// _log.info(getPrefix(requestId) + " oldhost " + oldhost + " newhost " + host + " dest " + destination);
//}
if (_log.shouldLog(Log.DEBUG)) { if (_log.shouldLog(Log.DEBUG)) {
_log.debug(getPrefix(requestId) + "METHOD: \"" + method + "\""); _log.debug(getPrefix(requestId) + "NEWREQ: \"" + line + "\"");
_log.debug(getPrefix(requestId) + "PROTOC: \"" + protocol + "\"");
_log.debug(getPrefix(requestId) + "HOST : \"" + host + "\""); _log.debug(getPrefix(requestId) + "HOST : \"" + host + "\"");
_log.debug(getPrefix(requestId) + "DEST : \"" + destination + "\""); _log.debug(getPrefix(requestId) + "DEST : \"" + destination + "\"");
} }
@@ -763,7 +810,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if (method == null || destination == null) { if (method == null || destination == null) {
//l.log("No HTTP method found in the request."); //l.log("No HTTP method found in the request.");
if (out != null) { if (out != null) {
if (protocol != null && "http://".equals(protocol.toLowerCase(Locale.US))) if (protocol != null && "http".equals(protocol.toLowerCase(Locale.US)))
out.write(getErrorPage("denied", ERR_REQUEST_DENIED)); out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
else else
out.write(getErrorPage("protocol", ERR_BAD_PROTOCOL)); out.write(getErrorPage("protocol", ERR_BAD_PROTOCOL));
@@ -794,11 +841,11 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
// Ignore all the headers // Ignore all the headers
if (usingInternalServer) { if (usingInternalServer) {
// disable the add form if address helper is disabled // disable the add form if address helper is disabled
if (targetRequest.startsWith(LOCAL_SERVER + "/add?") && if (internalPath.equals("/add") &&
Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) { Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
out.write(ERR_HELPER_DISABLED); out.write(ERR_HELPER_DISABLED);
} else { } else {
serveLocalFile(out, method, targetRequest, _proxyNonce); LocalHTTPServer.serveLocalFile(out, method, internalPath, internalRawQuery, _proxyNonce);
} }
s.close(); s.close();
return; return;
@@ -812,9 +859,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
String addressHelper = addressHelpers.get(destination.toLowerCase(Locale.US)); String addressHelper = addressHelpers.get(destination.toLowerCase(Locale.US));
if (addressHelper != null) { if (addressHelper != null) {
clientDest = _context.namingService().lookup(addressHelper); clientDest = _context.namingService().lookup(addressHelper);
// remove bad entries if (clientDest == null) {
if (clientDest == null) // remove bad entries
addressHelpers.remove(destination.toLowerCase(Locale.US)); addressHelpers.remove(destination.toLowerCase(Locale.US));
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix(requestId) + "Could not find destination for " + addressHelper);
byte[] header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
writeErrorMessage(header, out, targetRequest, false, destination, null);
s.close();
return;
}
} else if ("i2p".equals(host)) { } else if ("i2p".equals(host)) {
clientDest = null; clientDest = null;
} else if (destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) { } else if (destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
@@ -865,7 +919,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if (ahelperNew && "GET".equals(method) && if (ahelperNew && "GET".equals(method) &&
(userAgent == null || !userAgent.startsWith("Wget")) && (userAgent == null || !userAgent.startsWith("Wget")) &&
!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) { !Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
writeHelperSaveForm(out, destination, ahelperKey, protocol + targetRequest); writeHelperSaveForm(out, destination, ahelperKey, targetRequest);
s.close(); s.close();
return; return;
} }
@@ -875,10 +929,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
// This also prevents the not-found error page from looking bad // This also prevents the not-found error page from looking bad
// Syndie can't handle a redirect of a POST // Syndie can't handle a redirect of a POST
if (ahelperPresent && !"POST".equals(method)) { if (ahelperPresent && !"POST".equals(method)) {
String uri = protocol + targetRequest; String uri = targetRequest;
int spc = uri.indexOf(" ");
if (spc >= 0)
uri = uri.substring(0, spc);
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Auto redirecting to " + uri); _log.debug("Auto redirecting to " + uri);
out.write(("HTTP/1.1 301 Address Helper Accepted\r\n"+ out.write(("HTTP/1.1 301 Address Helper Accepted\r\n"+
@@ -928,10 +979,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
private void writeHelperSaveForm(OutputStream out, String destination, String ahelperKey, String targetRequest) throws IOException { private void writeHelperSaveForm(OutputStream out, String destination, String ahelperKey, String targetRequest) throws IOException {
if (out == null) if (out == null)
return; return;
// strip HTTP/1.1
int protopos = targetRequest.indexOf(" ");
if (protopos >= 0)
targetRequest = targetRequest.substring(0, protopos);
byte[] header = getErrorPage("ahelper-new", ERR_AHELPER_NEW); byte[] header = getErrorPage("ahelper-new", ERR_AHELPER_NEW);
out.write(header); out.write(header);
out.write(("<table><tr><td class=\"mediumtags\" align=\"right\">" + _("Host") + "</td><td class=\"mediumtags\">" + destination + "</td></tr>\n" + out.write(("<table><tr><td class=\"mediumtags\" align=\"right\">" + _("Host") + "</td><td class=\"mediumtags\">" + destination + "</td></tr>\n" +
@@ -939,6 +986,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"<textarea rows=\"1\" style=\"height: 4em; min-width: 0; min-height: 0;\" cols=\"70\" wrap=\"off\" readonly=\"readonly\" >" + "<textarea rows=\"1\" style=\"height: 4em; min-width: 0; min-height: 0;\" cols=\"70\" wrap=\"off\" readonly=\"readonly\" >" +
ahelperKey + "</textarea></td></tr></table>\n" + ahelperKey + "</textarea></td></tr></table>\n" +
"<hr><div class=\"formaction\">"+ "<hr><div class=\"formaction\">"+
// FIXME if there is a query remaining it is lost
"<form method=\"GET\" action=\"" + targetRequest + "\">" + "<form method=\"GET\" action=\"" + targetRequest + "\">" +
"<button type=\"submit\" class=\"go\">" + _("Continue to {0} without saving", destination) + "</button>" + "<button type=\"submit\" class=\"go\">" + _("Continue to {0} without saving", destination) + "</button>" +
"</form>\n<form method=\"GET\" action=\"http://" + LOCAL_SERVER + "/add\">" + "</form>\n<form method=\"GET\" action=\"http://" + LOCAL_SERVER + "/add\">" +
@@ -1046,7 +1094,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
// we won't ever get here // we won't ever get here
} }
private static void writeFooter(OutputStream out) throws IOException { /**
* Public only for LocalHTTPServer, not for general use
*/
public static void writeFooter(OutputStream out) throws IOException {
// the css is hiding this div for now, but we'll keep it here anyway // the css is hiding this div for now, but we'll keep it here anyway
out.write("<div class=\"proxyfooter\"><p><i>I2P HTTP Proxy Server<br>Generated on: ".getBytes()); out.write("<div class=\"proxyfooter\"><p><i>I2P HTTP Proxy Server<br>Generated on: ".getBytes());
out.write(new Date().toString().getBytes()); out.write(new Date().toString().getBytes());
@@ -1091,15 +1142,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if (out != null) { if (out != null) {
out.write(errMessage); out.write(errMessage);
if (targetRequest != null) { if (targetRequest != null) {
int protopos = targetRequest.indexOf(" "); String uri = targetRequest;
String uri; out.write("<a href=\"".getBytes());
if (protopos >= 0)
uri = targetRequest.substring(0, protopos);
else
uri = targetRequest;
out.write("<a href=\"http://".getBytes());
out.write(uri.getBytes()); out.write(uri.getBytes());
out.write("\">http://".getBytes()); out.write("\">".getBytes());
out.write(uri.getBytes()); out.write(uri.getBytes());
out.write("</a>".getBytes()); out.write("</a>".getBytes());
if (usingWWWProxy) { if (usingWWWProxy) {
@@ -1193,23 +1239,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
} }
} }
****/ ****/
return protocol.toLowerCase(Locale.US).equals("http://"); return protocol.toLowerCase(Locale.US).equals("http");
} }
private final static byte[] ERR_404 =
("HTTP/1.1 404 Not Found\r\n"+
"Content-Type: text/plain\r\n"+
"\r\n"+
"HTTP Proxy local file not found")
.getBytes();
private final static byte[] ERR_ADD =
("HTTP/1.1 409 Bad\r\n"+
"Content-Type: text/plain\r\n"+
"\r\n"+
"Add to addressbook failed - bad parameters")
.getBytes();
private final static byte[] ERR_HELPER_DISABLED = private final static byte[] ERR_HELPER_DISABLED =
("HTTP/1.1 403 Disabled\r\n"+ ("HTTP/1.1 403 Disabled\r\n"+
"Content-Type: text/plain\r\n"+ "Content-Type: text/plain\r\n"+
@@ -1218,185 +1250,127 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
.getBytes(); .getBytes();
/** /**
* Very simple web server. * Change various parts of the URI.
* String parameters are all non-encoded.
* *
* Serve local files in the docs/ directory, for CSS and images in * Scheme always preserved.
* error pages, using the reserved address proxy.i2p * Userinfo always cleared.
* (similar to p.p in privoxy). * Host changed if non-null.
* This solves the problems with including links to the router console, * Port changed if non-zero.
* as assuming the router console is at 127.0.0.1 leads to broken * Path changed if non-null.
* links if it isn't. * Query always preserved.
* Fragment always cleared.
* *
* Ignore all request headers (If-Modified-Since, etc.) * @since 0.9
*
* There is basic protection here -
* FileUtil.readFile() prevents traversal above the base directory -
* but inproxy/gateway ops would be wise to block proxy.i2p to prevent
* exposing the docs/ directory or perhaps other issues through
* uncaught vulnerabilities.
* Restrict to the /themes/ directory for now.
*
* @param targetRequest "proxy.i2p/themes/foo.png HTTP/1.1"
*/ */
private static void serveLocalFile(OutputStream out, String method, String targetRequest, String proxyNonce) { private static URI changeURI(URI uri, String host, int port, String path) throws URISyntaxException {
//System.err.println("targetRequest: \"" + targetRequest + "\""); return new URI(uri.getScheme(),
// a home page message for the curious... null,
if (targetRequest.startsWith(LOCAL_SERVER + "/ ")) { host != null ? host : uri.getHost(),
try { port != 0 ? port : uri.getPort(),
out.write(("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nCache-Control: max-age=86400\r\n\r\nI2P HTTP proxy OK").getBytes()); path != null ? path : uri.getPath(),
out.flush(); // FIXME this breaks encoded =, &
} catch (IOException ioe) {} uri.getQuery(),
return; null);
}
if ((method.equals("GET") || method.equals("HEAD")) &&
targetRequest.startsWith(LOCAL_SERVER + "/themes/") &&
!targetRequest.contains("..")) {
int space = targetRequest.indexOf(' ');
String filename = null;
try {
filename = targetRequest.substring(LOCAL_SERVER.length() + 8, space); // "/themes/".length
} catch (IndexOutOfBoundsException ioobe) {
return;
}
// theme hack
if (filename.startsWith("console/default/"))
filename = filename.replaceFirst("default", I2PAppContext.getGlobalContext().getProperty("routerconsole.theme", "light"));
File themesDir = new File(_errorDir, "themes");
File file = new File(themesDir, filename);
if (file.exists() && !file.isDirectory()) {
String type;
if (filename.endsWith(".css"))
type = "text/css";
else if (filename.endsWith(".ico"))
type = "image/x-icon";
else if (filename.endsWith(".png"))
type = "image/png";
else if (filename.endsWith(".jpg"))
type = "image/jpeg";
else type = "text/html";
try {
out.write("HTTP/1.1 200 OK\r\nContent-Type: ".getBytes());
out.write(type.getBytes());
out.write("\r\nCache-Control: max-age=86400\r\n\r\n".getBytes());
FileUtil.readFile(filename, themesDir.getAbsolutePath(), out);
} catch (IOException ioe) {}
return;
}
}
// Add to addressbook (form submit)
// Parameters are url, host, dest, nonce, and master | router | private.
// Do the add and redirect.
if (targetRequest.startsWith(LOCAL_SERVER + "/add?")) {
int spc = targetRequest.indexOf(' ');
String query = targetRequest.substring(LOCAL_SERVER.length() + 5, spc); // "/add?".length()
Map<String, String> opts = new HashMap(8);
StringTokenizer tok = new StringTokenizer(query, "=&;");
while (tok.hasMoreTokens()) {
String k = tok.nextToken();
if (!tok.hasMoreTokens())
break;
String v = tok.nextToken();
opts.put(decode(k), decode(v));
}
String url = opts.get("url");
String host = opts.get("host");
String b64Dest = opts.get("dest");
String nonce = opts.get("nonce");
String book = "privatehosts.txt";
if (opts.get("master") != null)
book = "userhosts.txt";
else if (opts.get("router") != null)
book = "hosts.txt";
Destination dest = null;
if (b64Dest != null) {
try {
dest = new Destination(b64Dest);
} catch (DataFormatException dfe) {
System.err.println("Bad dest to save?" + b64Dest);
}
}
//System.err.println("url : \"" + url + "\"");
//System.err.println("host : \"" + host + "\"");
//System.err.println("b64dest : \"" + b64Dest + "\"");
//System.err.println("book : \"" + book + "\"");
//System.err.println("nonce : \"" + nonce + "\"");
if (proxyNonce.equals(nonce) && url != null && host != null && dest != null) {
try {
NamingService ns = I2PAppContext.getGlobalContext().namingService();
Properties nsOptions = new Properties();
nsOptions.setProperty("list", book);
nsOptions.setProperty("s", _("Added via address helper"));
boolean success = ns.put(host, dest, nsOptions);
writeRedirectPage(out, success, host, book, url);
return;
} catch (IOException ioe) {}
}
try {
out.write(ERR_ADD);
out.flush();
} catch (IOException ioe) {}
return;
}
try {
out.write(ERR_404);
out.flush();
} catch (IOException ioe) {}
}
/** @since 0.8.7 */
private static void writeRedirectPage(OutputStream out, boolean success, String host, String book, String url) throws IOException {
out.write(("HTTP/1.1 200 OK\r\n"+
"Content-Type: text/html; charset=UTF-8\r\n"+
"\r\n"+
"<html><head>"+
"<title>" + _("Redirecting to {0}", host) + "</title>\n" +
"<link rel=\"shortcut icon\" href=\"http://proxy.i2p/themes/console/images/favicon.ico\" >\n" +
"<link href=\"http://proxy.i2p/themes/console/default/console.css\" rel=\"stylesheet\" type=\"text/css\" >\n" +
"<meta http-equiv=\"Refresh\" content=\"1; url=" + url + "\">\n" +
"</head><body>\n" +
"<div class=logo>\n" +
"<a href=\"http://127.0.0.1:7657/\" title=\"" + _("Router Console") + "\"><img src=\"http://proxy.i2p/themes/console/images/i2plogo.png\" alt=\"I2P Router Console\" border=\"0\"></a><hr>\n" +
"<a href=\"http://127.0.0.1:7657/config\">" + _("Configuration") + "</a> <a href=\"http://127.0.0.1:7657/help.jsp\">" + _("Help") + "</a> <a href=\"http://127.0.0.1:7657/susidns/index\">" + _("Addressbook") + "</a>\n" +
"</div>" +
"<div class=warning id=warning>\n" +
"<h3>" +
(success ?
_("Saved {0} to the {1} addressbook, redirecting now.", host, book) :
_("Failed to save {0} to the {1} addressbook, redirecting now.", host, book)) +
"</h3>\n<p><a href=\"" + url + "\">" +
_("Click here if you are not redirected automatically.") +
"</a></p></div>").getBytes("UTF-8"));
writeFooter(out);
out.flush();
} }
/** /**
* Decode %xx encoding * Replace query in the URI.
* @since 0.8.7 * Userinfo cleared if uri contained a query.
* Fragment cleared if uri contained a query.
*
* @param query an ENCODED query, removed if null
* @since 0.9
*/ */
private static String decode(String s) { private static URI replaceQuery(URI uri, String query) throws URISyntaxException {
if (!s.contains("%")) URI rv = uri;
return s; if (rv.getRawQuery() != null) {
StringBuilder buf = new StringBuilder(s.length()); rv = new URI(rv.getScheme(),
for (int i = 0; i < s.length(); i++) { null,
char c = s.charAt(i); uri.getHost(),
if (c != '%') { uri.getPort(),
buf.append(c); uri.getPath(),
} else { null,
try { null);
buf.append((char) Integer.parseInt(s.substring(++i, (++i) + 1), 16));
} catch (IndexOutOfBoundsException ioobe) {
break;
} catch (NumberFormatException nfe) {
break;
}
}
} }
return buf.toString(); if (query != null) {
String newURI = rv.toASCIIString() + '?' + query;
rv = new URI(newURI);
}
return rv;
} }
/**
* Remove the address helper from an encoded query.
*
* @param query an ENCODED query, removed if null
* @return rv[0] is ENCODED query with helper removed, non-null but possibly empty;
* rv[1] is DECODED helper value, non-null but possibly empty;
* rv null if no helper present
* @since 0.9
*/
private static String[] removeHelper(String query) {
int keystart = 0;
int valstart = -1;
String key = null;
for (int i = 0; i <= query.length(); i++) {
char c = i < query.length() ? query.charAt(i) : '&';
if (c == ';' || c == '&') {
// end of key or value
if (valstart < 0)
key = query.substring(keystart, i);
String decodedKey = LocalHTTPServer.decode(key);
if (decodedKey.equals(HELPER_PARAM)) {
String newQuery = keystart > 0 ? query.substring(0, keystart - 1) : "";
if (i < query.length() - 1) {
if (keystart > 0)
newQuery += query.substring(i);
else
newQuery += query.substring(i + 1);
}
String value = valstart >= 0 ? query.substring(valstart, i) : "";
String helperValue = LocalHTTPServer.decode(value);
return new String[] { newQuery, helperValue };
}
keystart = i + 1;
valstart = -1;
} else if (c == '=') {
// end of key
key = query.substring(keystart, i);
valstart = i + 1;
}
}
return null;
}
/****
private static String[] tests = {
"", "foo", "foo=bar", "&", "&=&", "===", "&&",
"i2paddresshelper=foo",
"i2paddresshelpe=foo",
"2paddresshelper=foo",
"i2paddresshelper=%66oo",
"%692paddresshelper=foo",
"i2paddresshelper=foo&a=b",
"a=b&i2paddresshelper=foo",
"a=b&i2paddresshelper&c=d",
"a=b&i2paddresshelper=foo&c=d",
"a=b;i2paddresshelper=foo;c=d",
"a=b&i2paddresshelper=foo&c"
};
public static void main(String[] args) {
for (int i = 0; i < tests.length; i++) {
String[] s = removeHelper(tests[i]);
if (s != null)
System.out.println("Test \"" + tests[i] + "\" q=\"" + s[0] + "\" h=\"" + s[1] + "\"");
else
System.out.println("Test \"" + tests[i] + "\" no match");
}
}
****/
/** */
private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.web.messages"; private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.web.messages";
/** lang in routerconsole.lang property, else current locale */ /** lang in routerconsole.lang property, else current locale */

View File

@@ -0,0 +1,247 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel.localServer;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.client.naming.NamingService;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.i2ptunnel.I2PTunnelHTTPClient;
import net.i2p.util.FileUtil;
import net.i2p.util.Log;
import net.i2p.util.Translate;
/**
* Very simple web server.
*
* Serve local files in the docs/ directory, for CSS and images in
* error pages, using the reserved address proxy.i2p
* (similar to p.p in privoxy).
* This solves the problems with including links to the router console,
* as assuming the router console is at 127.0.0.1 leads to broken
* links if it isn't.
*
* @since 0.7.6, moved from I2PTunnelHTTPClient in 0.9
*/
public abstract class LocalHTTPServer {
private final static byte[] ERR_404 =
("HTTP/1.1 404 Not Found\r\n"+
"Content-Type: text/plain\r\n"+
"\r\n"+
"HTTP Proxy local file not found")
.getBytes();
private final static byte[] ERR_ADD =
("HTTP/1.1 409 Bad\r\n"+
"Content-Type: text/plain\r\n"+
"\r\n"+
"Add to addressbook failed - bad parameters")
.getBytes();
/**
* Very simple web server.
*
* Serve local files in the docs/ directory, for CSS and images in
* error pages, using the reserved address proxy.i2p
* (similar to p.p in privoxy).
* This solves the problems with including links to the router console,
* as assuming the router console is at 127.0.0.1 leads to broken
* links if it isn't.
*
* Ignore all request headers (If-Modified-Since, etc.)
*
* There is basic protection here -
* FileUtil.readFile() prevents traversal above the base directory -
* but inproxy/gateway ops would be wise to block proxy.i2p to prevent
* exposing the docs/ directory or perhaps other issues through
* uncaught vulnerabilities.
* Restrict to the /themes/ directory for now.
*
* @param targetRequest decoded path only, non-null
* @param query raw (encoded), may be null
*/
public static void serveLocalFile(OutputStream out, String method, String targetRequest, String query, String proxyNonce) {
//System.err.println("targetRequest: \"" + targetRequest + "\"");
// a home page message for the curious...
if (targetRequest.equals("/")) {
try {
out.write(("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nCache-Control: max-age=86400\r\n\r\nI2P HTTP proxy OK").getBytes());
out.flush();
} catch (IOException ioe) {}
return;
}
if ((method.equals("GET") || method.equals("HEAD")) &&
targetRequest.startsWith("/themes/") &&
!targetRequest.contains("..")) {
String filename = null;
try {
filename = targetRequest.substring(8); // "/themes/".length
} catch (IndexOutOfBoundsException ioobe) {
return;
}
// theme hack
if (filename.startsWith("console/default/"))
filename = filename.replaceFirst("default", I2PAppContext.getGlobalContext().getProperty("routerconsole.theme", "light"));
File themesDir = new File(I2PAppContext.getGlobalContext().getBaseDir(), "docs/themes");
File file = new File(themesDir, filename);
if (file.exists() && !file.isDirectory()) {
String type;
if (filename.endsWith(".css"))
type = "text/css";
else if (filename.endsWith(".ico"))
type = "image/x-icon";
else if (filename.endsWith(".png"))
type = "image/png";
else if (filename.endsWith(".jpg"))
type = "image/jpeg";
else type = "text/html";
try {
out.write("HTTP/1.1 200 OK\r\nContent-Type: ".getBytes());
out.write(type.getBytes());
out.write("\r\nCache-Control: max-age=86400\r\n\r\n".getBytes());
FileUtil.readFile(filename, themesDir.getAbsolutePath(), out);
} catch (IOException ioe) {}
return;
}
}
// Add to addressbook (form submit)
// Parameters are url, host, dest, nonce, and master | router | private.
// Do the add and redirect.
if (targetRequest.equals("/add")) {
Map<String, String> opts = new HashMap(8);
// this only works if all keys are followed by =value
StringTokenizer tok = new StringTokenizer(query, "=&;");
while (tok.hasMoreTokens()) {
String k = tok.nextToken();
if (!tok.hasMoreTokens())
break;
String v = tok.nextToken();
opts.put(decode(k), decode(v));
}
String url = opts.get("url");
String host = opts.get("host");
String b64Dest = opts.get("dest");
String nonce = opts.get("nonce");
String book = "privatehosts.txt";
if (opts.get("master") != null)
book = "userhosts.txt";
else if (opts.get("router") != null)
book = "hosts.txt";
Destination dest = null;
if (b64Dest != null) {
try {
dest = new Destination(b64Dest);
} catch (DataFormatException dfe) {
System.err.println("Bad dest to save?" + b64Dest);
}
}
//System.err.println("url : \"" + url + "\"");
//System.err.println("host : \"" + host + "\"");
//System.err.println("b64dest : \"" + b64Dest + "\"");
//System.err.println("book : \"" + book + "\"");
//System.err.println("nonce : \"" + nonce + "\"");
if (proxyNonce.equals(nonce) && url != null && host != null && dest != null) {
try {
NamingService ns = I2PAppContext.getGlobalContext().namingService();
Properties nsOptions = new Properties();
nsOptions.setProperty("list", book);
nsOptions.setProperty("s", _("Added via address helper"));
boolean success = ns.put(host, dest, nsOptions);
writeRedirectPage(out, success, host, book, url);
return;
} catch (IOException ioe) {}
}
try {
out.write(ERR_ADD);
out.flush();
} catch (IOException ioe) {}
return;
}
try {
out.write(ERR_404);
out.flush();
} catch (IOException ioe) {}
}
/** @since 0.8.7 */
private static void writeRedirectPage(OutputStream out, boolean success, String host, String book, String url) throws IOException {
out.write(("HTTP/1.1 200 OK\r\n"+
"Content-Type: text/html; charset=UTF-8\r\n"+
"\r\n"+
"<html><head>"+
"<title>" + _("Redirecting to {0}", host) + "</title>\n" +
"<link rel=\"shortcut icon\" href=\"http://proxy.i2p/themes/console/images/favicon.ico\" >\n" +
"<link href=\"http://proxy.i2p/themes/console/default/console.css\" rel=\"stylesheet\" type=\"text/css\" >\n" +
"<meta http-equiv=\"Refresh\" content=\"1; url=" + url + "\">\n" +
"</head><body>\n" +
"<div class=logo>\n" +
"<a href=\"http://127.0.0.1:7657/\" title=\"" + _("Router Console") + "\"><img src=\"http://proxy.i2p/themes/console/images/i2plogo.png\" alt=\"I2P Router Console\" border=\"0\"></a><hr>\n" +
"<a href=\"http://127.0.0.1:7657/config\">" + _("Configuration") + "</a> <a href=\"http://127.0.0.1:7657/help.jsp\">" + _("Help") + "</a> <a href=\"http://127.0.0.1:7657/susidns/index\">" + _("Addressbook") + "</a>\n" +
"</div>" +
"<div class=warning id=warning>\n" +
"<h3>" +
(success ?
_("Saved {0} to the {1} addressbook, redirecting now.", host, book) :
_("Failed to save {0} to the {1} addressbook, redirecting now.", host, book)) +
"</h3>\n<p><a href=\"" + url + "\">" +
_("Click here if you are not redirected automatically.") +
"</a></p></div>").getBytes("UTF-8"));
I2PTunnelHTTPClient.writeFooter(out);
out.flush();
}
/**
* Decode %xx encoding
* @since 0.8.7
*/
public static String decode(String s) {
if (!s.contains("%"))
return s;
StringBuilder buf = new StringBuilder(s.length());
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c != '%') {
buf.append(c);
} else {
try {
buf.append((char) Integer.parseInt(s.substring(++i, (++i) + 1), 16));
} catch (IndexOutOfBoundsException ioobe) {
break;
} catch (NumberFormatException nfe) {
break;
}
}
}
return buf.toString();
}
private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.web.messages";
/** lang in routerconsole.lang property, else current locale */
protected static String _(String key) {
return Translate.getString(key, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
}
/** {0} */
protected static String _(String key, Object o) {
return Translate.getString(key, o, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
}
/** {0} and {1} */
protected static String _(String key, Object o, Object o2) {
return Translate.getString(key, o, o2, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
}
}

View File

@@ -1,6 +1,10 @@
<% <%
// NOTE: Do the header carefully so there is no whitespace before the <?xml... line // NOTE: Do the header carefully so there is no whitespace before the <?xml... line
// http://www.crazysquirrel.com/computing/general/form-encoding.jspx
if (request.getCharacterEncoding() == null)
request.setCharacterEncoding("UTF-8");
%><%@page pageEncoding="UTF-8" %><%@page pageEncoding="UTF-8"
%><%@page contentType="text/html" import="net.i2p.i2ptunnel.web.EditBean" %><%@page contentType="text/html" import="net.i2p.i2ptunnel.web.EditBean"
%><?xml version="1.0" encoding="UTF-8"?> %><?xml version="1.0" encoding="UTF-8"?>

View File

@@ -117,6 +117,7 @@
<property name="workspace.changes.tr" value="" /> <property name="workspace.changes.tr" value="" />
<jar destfile="./jettylib/org.mortbay.jetty.jar" basedir="./build/obj" includes="**/*.class" update="true" > <jar destfile="./jettylib/org.mortbay.jetty.jar" basedir="./build/obj" includes="**/*.class" update="true" >
<manifest> <manifest>
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" />

View File

@@ -51,6 +51,7 @@
<jar destfile="./build/mstreaming.jar" basedir="./build/obj" includes="**/*.class" > <jar destfile="./build/mstreaming.jar" basedir="./build/obj" includes="**/*.class" >
<manifest> <manifest>
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" />

View File

@@ -92,6 +92,7 @@
<!-- DTG added in 0.8.4, not in the classpath for very old installs, before we changed wrapper.config to specify * --> <!-- DTG added in 0.8.4, not in the classpath for very old installs, before we changed wrapper.config to specify * -->
<attribute name="Class-Path" value="i2p.jar router.jar jrobin.jar desktopgui.jar" /> <attribute name="Class-Path" value="i2p.jar router.jar jrobin.jar desktopgui.jar" />
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.j.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.j.tr}" />
@@ -172,6 +173,7 @@
basedir="../jsp/" excludes="web.xml, *.css, **/*.java, *.jsp, *.jsi, web-fragment.xml, web-out.xml"> basedir="../jsp/" excludes="web.xml, *.css, **/*.java, *.jsp, *.jsi, web-fragment.xml, web-out.xml">
<manifest> <manifest>
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.w.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.w.tr}" />

View File

@@ -52,7 +52,7 @@ public class ConfigClientsHelper extends HelperBase {
if ((mode == 0 && disabled) || if ((mode == 0 && disabled) ||
(mode == 1 && (!disabled) && (!ssl)) || (mode == 1 && (!disabled) && (!ssl)) ||
(mode == 2 && (!disabled) && ssl)) (mode == 2 && (!disabled) && ssl))
return "checked=\"true\""; return "checked=\"checked\"";
return ""; return "";
} }
@@ -60,7 +60,7 @@ public class ConfigClientsHelper extends HelperBase {
public String getAuth() { public String getAuth() {
boolean enabled = _context.getBooleanProperty(PROP_AUTH); boolean enabled = _context.getBooleanProperty(PROP_AUTH);
if (enabled) if (enabled)
return "checked=\"true\""; return "checked=\"checked\"";
return ""; return "";
} }
@@ -253,9 +253,9 @@ public class ConfigClientsHelper extends HelperBase {
} }
buf.append("</td><td align=\"center\" width=\"10%\"><input type=\"checkbox\" class=\"optbox\" name=\"").append(index).append(".enabled\" value=\"true\" "); buf.append("</td><td align=\"center\" width=\"10%\"><input type=\"checkbox\" class=\"optbox\" name=\"").append(index).append(".enabled\" value=\"true\" ");
if (enabled) { if (enabled) {
buf.append("checked=\"true\" "); buf.append("checked=\"checked\" ");
if (ro) if (ro)
buf.append("disabled=\"true\" "); buf.append("disabled=\"disabled\" ");
} }
buf.append("></td><td align=\"center\" width=\"15%\">"); buf.append("></td><td align=\"center\" width=\"15%\">");
// The icons were way too much, so there's an X in each button class, // The icons were way too much, so there's an X in each button class,

View File

@@ -80,7 +80,7 @@ public class ConfigLoggingHelper extends HelperBase {
String l = levels[i]; String l = levels[i];
buf.append("<option value=\"").append(l).append('\"'); buf.append("<option value=\"").append(l).append('\"');
if (l.equals(cur)) if (l.equals(cur))
buf.append(" selected=\"true\""); buf.append(" selected=\"selected\"");
buf.append('>').append(_(l)).append("</option>\n"); buf.append('>').append(_(l)).append("</option>\n");
} }
@@ -121,7 +121,7 @@ public class ConfigLoggingHelper extends HelperBase {
StringBuilder buf = new StringBuilder(65536); StringBuilder buf = new StringBuilder(65536);
buf.append("<select name=\"newlogclass\">\n" + buf.append("<select name=\"newlogclass\">\n" +
"<option value=\"\" selected=\"true\">") "<option value=\"\" selected=\"selected\">")
.append(_("Select a class to add")) .append(_("Select a class to add"))
.append("</option>\n"); .append("</option>\n");

View File

@@ -20,8 +20,8 @@ public class ConfigNetHelper extends HelperBase {
public final static String PROP_I2NP_NTCP_PORT = "i2np.ntcp.port"; public final static String PROP_I2NP_NTCP_PORT = "i2np.ntcp.port";
public final static String PROP_I2NP_NTCP_AUTO_PORT = "i2np.ntcp.autoport"; public final static String PROP_I2NP_NTCP_AUTO_PORT = "i2np.ntcp.autoport";
public final static String PROP_I2NP_NTCP_AUTO_IP = "i2np.ntcp.autoip"; public final static String PROP_I2NP_NTCP_AUTO_IP = "i2np.ntcp.autoip";
private final static String CHECKED = " checked=\"true\" "; private final static String CHECKED = " checked=\"checked\" ";
private final static String DISABLED = " disabled=\"true\" "; private final static String DISABLED = " disabled=\"disabled\" ";
public String getUdphostname() { public String getUdphostname() {
return _context.getProperty(UDPTransport.PROP_EXTERNAL_HOST, ""); return _context.getProperty(UDPTransport.PROP_EXTERNAL_HOST, "");
@@ -250,7 +250,7 @@ public class ConfigNetHelper extends HelperBase {
} }
buf.append("<option style=\"text-align: right;\" value=\"").append(val).append("\" "); buf.append("<option style=\"text-align: right;\" value=\"").append(val).append("\" ");
if (pct == val) { if (pct == val) {
buf.append("selected=\"true\" "); buf.append("selected=\"selected\" ");
found = true; found = true;
} }
buf.append(">").append(val).append("%</option>\n"); buf.append(">").append(val).append("%</option>\n");

View File

@@ -56,7 +56,7 @@ public class ConfigReseedHelper extends HelperBase {
if ((mode == 0 && (!disabled) && (!required)) || if ((mode == 0 && (!disabled) && (!required)) ||
(mode == 1 && (!disabled) && required) || (mode == 1 && (!disabled) && required) ||
(mode == 2 && disabled)) (mode == 2 && disabled))
return "checked=\"true\""; return "checked=\"checked\"";
return ""; return "";
} }
@@ -82,7 +82,7 @@ public class ConfigReseedHelper extends HelperBase {
private String checked(String prop) { private String checked(String prop) {
boolean enabled = _context.getBooleanProperty(prop); boolean enabled = _context.getBooleanProperty(prop);
if (enabled) if (enabled)
return "checked=\"true\""; return "checked=\"checked\"";
return ""; return "";
} }

View File

@@ -59,8 +59,16 @@ public class ConfigStatsHelper extends HelperBase {
_filters.add(tok.nextToken().trim()); _filters.add(tok.nextToken().trim());
} }
public ConfigStatsHelper() {} /**
* Just hide for everybody unless already set.
* To enable set advanced config stat.logFilters=foo before starting...
* it has to be set at startup anyway for logging to be enabled at all
* @since 0.9
*/
public boolean shouldShowLog() {
return !_filters.isEmpty();
}
public String getFilename() { return _context.statManager().getStatFile(); } public String getFilename() { return _context.statManager().getStatFile(); }
/** /**

View File

@@ -172,7 +172,7 @@ public class ConfigTunnelsHelper extends HelperBase {
if (!props.isEmpty()) { if (!props.isEmpty()) {
buf.append("<tr><td align=\"right\" class=\"mediumtags\">" + _("Inbound options") + ":</td>\n" + buf.append("<tr><td align=\"right\" class=\"mediumtags\">" + _("Inbound options") + ":</td>\n" +
"<td colspan=\"2\" align=\"center\"><input name=\"").append(index); "<td colspan=\"2\" align=\"center\"><input name=\"").append(index);
buf.append(".inboundOptions\" type=\"text\" size=\"32\" disabled=\"true\" " + buf.append(".inboundOptions\" type=\"text\" size=\"32\" disabled=\"disabled\" " +
"value=\""); "value=\"");
for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) { for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) {
String prop = (String)iter.next(); String prop = (String)iter.next();
@@ -185,7 +185,7 @@ public class ConfigTunnelsHelper extends HelperBase {
if (!props.isEmpty()) { if (!props.isEmpty()) {
buf.append("<tr><td align=\"right\" class=\"mediumtags\">" + _("Outbound options") + ":</td>\n" + buf.append("<tr><td align=\"right\" class=\"mediumtags\">" + _("Outbound options") + ":</td>\n" +
"<td colspan=\"2\" align=\"center\"><input name=\"").append(index); "<td colspan=\"2\" align=\"center\"><input name=\"").append(index);
buf.append(".outboundOptions\" type=\"text\" size=\"32\" disabled=\"true\" " + buf.append(".outboundOptions\" type=\"text\" size=\"32\" disabled=\"disabled\" " +
"value=\""); "value=\"");
for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) { for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) {
String prop = (String)iter.next(); String prop = (String)iter.next();
@@ -205,7 +205,7 @@ public class ConfigTunnelsHelper extends HelperBase {
for (int i = min; i <= max; i++) { for (int i = min; i <= max; i++) {
buf.append("<option value=\"").append(i).append("\" "); buf.append("<option value=\"").append(i).append("\" ");
if (i == now) if (i == now)
buf.append("selected=\"true\" "); buf.append("selected=\"selected\" ");
buf.append(">").append(ngettext(DUMMY1 + name, DUMMY2 + name + 's', i)); buf.append(">").append(ngettext(DUMMY1 + name, DUMMY2 + name + 's', i));
buf.append("</option>\n"); buf.append("</option>\n");
} }

View File

@@ -14,7 +14,7 @@ public class ConfigUIHelper extends HelperBase {
for (String theme : themes) { for (String theme : themes) {
buf.append("<input type=\"radio\" class=\"optbox\" name=\"theme\" "); buf.append("<input type=\"radio\" class=\"optbox\" name=\"theme\" ");
if (theme.equals(current)) if (theme.equals(current))
buf.append("checked=\"true\" "); buf.append("checked=\"checked\" ");
buf.append("value=\"").append(theme).append("\">").append(_(theme)).append("<br>\n"); buf.append("value=\"").append(theme).append("\">").append(_(theme)).append("<br>\n");
} }
return buf.toString(); return buf.toString();
@@ -73,7 +73,7 @@ public class ConfigUIHelper extends HelperBase {
// we use "lang" so it is set automagically in CSSHelper // we use "lang" so it is set automagically in CSSHelper
buf.append("<input type=\"radio\" class=\"optbox\" name=\"lang\" "); buf.append("<input type=\"radio\" class=\"optbox\" name=\"lang\" ");
if (langs[i].equals(current)) if (langs[i].equals(current))
buf.append("checked=\"true\" "); buf.append("checked=\"checked\" ");
buf.append("value=\"").append(langs[i]).append("\">") buf.append("value=\"").append(langs[i]).append("\">")
.append("<img height=\"11\" width=\"16\" alt=\"\" src=\"/flags.jsp?c=").append(flags[i]).append("\"> ") .append("<img height=\"11\" width=\"16\" alt=\"\" src=\"/flags.jsp?c=").append(flags[i]).append("\"> ")
.append(_(xlangs[i])).append("<br>\n"); .append(_(xlangs[i])).append("<br>\n");

View File

@@ -75,7 +75,7 @@ public class ConfigUpdateHelper extends HelperBase {
public String getUpdateThroughProxy() { public String getUpdateThroughProxy() {
String proxy = _context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY); String proxy = _context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY);
if (Boolean.valueOf(proxy).booleanValue()) if (Boolean.valueOf(proxy).booleanValue())
return "<input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"updateThroughProxy\" checked=\"true\" >"; return "<input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"updateThroughProxy\" checked=\"checked\" >";
else else
return "<input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"updateThroughProxy\" >"; return "<input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"updateThroughProxy\" >";
} }
@@ -83,7 +83,7 @@ public class ConfigUpdateHelper extends HelperBase {
public String getUpdateUnsigned() { public String getUpdateUnsigned() {
String foo = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_UNSIGNED); String foo = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_UNSIGNED);
if (Boolean.valueOf(foo).booleanValue()) if (Boolean.valueOf(foo).booleanValue())
return "<input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"updateUnsigned\" checked=\"true\" >"; return "<input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"updateUnsigned\" checked=\"checked\" >";
else else
return "<input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"updateUnsigned\" >"; return "<input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"updateUnsigned\" >";
} }
@@ -106,7 +106,7 @@ public class ConfigUpdateHelper extends HelperBase {
for (int i = 0; i < PERIODS.length; i++) { for (int i = 0; i < PERIODS.length; i++) {
buf.append("<option value=\"").append(PERIODS[i]); buf.append("<option value=\"").append(PERIODS[i]);
if (PERIODS[i] == ms) if (PERIODS[i] == ms)
buf.append("\" selected=\"true"); buf.append("\" selected=\"selected");
if (PERIODS[i] == -1) if (PERIODS[i] == -1)
buf.append("\">" + _("Never") + "</option>\n"); buf.append("\">" + _("Never") + "</option>\n");
@@ -128,22 +128,22 @@ public class ConfigUpdateHelper extends HelperBase {
buf.append("<option value=\"notify\""); buf.append("<option value=\"notify\"");
if ("notify".equals(policy) || _dontInstall) if ("notify".equals(policy) || _dontInstall)
buf.append(" selected=\"true\""); buf.append(" selected=\"selected\"");
buf.append('>').append(_("Notify only")).append("</option>"); buf.append('>').append(_("Notify only")).append("</option>");
buf.append("<option value=\"download\""); buf.append("<option value=\"download\"");
if (_dontInstall) if (_dontInstall)
buf.append(" disabled=\"true\""); buf.append(" disabled=\"disabled\"");
else if ("download".equals(policy)) else if ("download".equals(policy))
buf.append(" selected=\"true\""); buf.append(" selected=\"selected\"");
buf.append('>').append(_("Download and verify only")).append("</option>"); buf.append('>').append(_("Download and verify only")).append("</option>");
if (_context.hasWrapper()) { if (_context.hasWrapper()) {
buf.append("<option value=\"install\""); buf.append("<option value=\"install\"");
if (_dontInstall) if (_dontInstall)
buf.append(" disabled=\"true\""); buf.append(" disabled=\"disabled\"");
else if ("install".equals(policy)) else if ("install".equals(policy))
buf.append(" selected=\"true\""); buf.append(" selected=\"selected\"");
buf.append('>').append(_("Download, verify, and restart")).append("</option>"); buf.append('>').append(_("Download, verify, and restart")).append("</option>");
} }

View File

@@ -18,6 +18,7 @@ import java.util.jar.Manifest;
import net.i2p.crypto.SHA256Generator; import net.i2p.crypto.SHA256Generator;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.util.FileUtil;
/** /**
* Dump info on jars and wars * Dump info on jars and wars
@@ -29,7 +30,7 @@ public class FileDumpHelper extends HelperBase {
public String getFileSummary() { public String getFileSummary() {
StringBuilder buf = new StringBuilder(16*1024); StringBuilder buf = new StringBuilder(16*1024);
buf.append("<table><tr><th>File</th><th>Size</th><th>Date</th><th>SHA 256</th><th>Revision</th>" + buf.append("<table><tr><th>File</th><th>Size</th><th>Date</th><th>SHA 256</th><th>Revision</th>" +
"<th>JDK</th><th>Built</th><th>Mods</th></tr>"); "<th>JDK</th><th>Built</th><th>By</th><th>Mods</th></tr>");
// jars added in wrapper.config // jars added in wrapper.config
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
@@ -95,7 +96,9 @@ public class FileDumpHelper extends HelperBase {
buf.append((new Date(mod)).toString()); buf.append((new Date(mod)).toString());
else else
buf.append("<font color=\"red\">Not found</font>"); buf.append("<font color=\"red\">Not found</font>");
buf.append("</td><td>"); buf.append("</td><td align=\"center\">");
if (mod > 0 && !FileUtil.verifyZip(f))
buf.append("<font color=\"red\">CORRUPT</font><br>");
byte[] hash = sha256(f); byte[] hash = sha256(f);
if (hash != null) { if (hash != null) {
byte[] hh = new byte[16]; byte[] hh = new byte[16];
@@ -132,13 +135,17 @@ public class FileDumpHelper extends HelperBase {
buf.append(s); buf.append(s);
buf.append("</td><td>"); buf.append("</td><td>");
s = getAtt(att, "Build-Date"); s = getAtt(att, "Build-Date");
if (s != null)
buf.append(s);
buf.append("</td><td align=\"center\">");
s = getAtt(att, "Built-By");
if (s != null) if (s != null)
buf.append(s); buf.append(s);
buf.append("</td><td><font color=\"red\">"); buf.append("</td><td><font color=\"red\">");
s = getAtt(att, "Workspace-Changes"); s = getAtt(att, "Workspace-Changes");
if (s != null) if (s != null)
buf.append(s.replace(",", "<br>")); buf.append(s.replace(",", "<br>"));
buf.append("</font></td>"); buf.append("</font></td></tr>\n");
} }
private static byte[] sha256(File f) { private static byte[] sha256(File f) {

View File

@@ -20,6 +20,8 @@ public class GraphHelper extends FormHandler {
private int _height; private int _height;
private int _refreshDelaySeconds; private int _refreshDelaySeconds;
private boolean _persistent; private boolean _persistent;
private String _stat;
private int _end;
private static final String PROP_X = "routerconsole.graphX"; private static final String PROP_X = "routerconsole.graphX";
private static final String PROP_Y = "routerconsole.graphY"; private static final String PROP_Y = "routerconsole.graphY";
@@ -32,6 +34,10 @@ public class GraphHelper extends FormHandler {
private static final int DEFAULT_PERIODS = 60; private static final int DEFAULT_PERIODS = 60;
static final int MAX_X = 2048; static final int MAX_X = 2048;
static final int MAX_Y = 1024; static final int MAX_Y = 1024;
private static final int MIN_X = 200;
private static final int MIN_Y = 60;
private static final int MIN_C = 20;
private static final int MAX_C = SummaryListener.MAX_ROWS;
private static final int MIN_REFRESH = 15; private static final int MIN_REFRESH = 15;
/** set the defaults after we have a context */ /** set the defaults after we have a context */
@@ -64,17 +70,45 @@ public class GraphHelper extends FormHandler {
public void storeWriter(Writer out) { _out = out; } public void storeWriter(Writer out) { _out = out; }
public void setPeriodCount(String str) { public void setPeriodCount(String str) {
try { _periodCount = Integer.parseInt(str); } catch (NumberFormatException nfe) {} setC(str);
} }
public void setShowEvents(boolean b) { _showEvents = b; } /** @since 0.9 */
public void setE(String str) {
try {
_end = Math.max(0, Integer.parseInt(str));
} catch (NumberFormatException nfe) {}
}
/** @since 0.9 shorter parameter */
public void setC(String str) {
try {
_periodCount = Math.max(MIN_C, Math.min(Integer.parseInt(str), MAX_C));
} catch (NumberFormatException nfe) {}
}
public void setShowEvents(String b) { _showEvents = !"false".equals(b); }
public void setHeight(String str) { public void setHeight(String str) {
try { _height = Math.min(Integer.parseInt(str), MAX_Y); } catch (NumberFormatException nfe) {} setH(str);
}
/** @since 0.9 shorter parameter */
public void setH(String str) {
try {
_height = Math.max(MIN_Y, Math.min(Integer.parseInt(str), MAX_Y));
} catch (NumberFormatException nfe) {}
} }
public void setWidth(String str) { public void setWidth(String str) {
try { _width = Math.min(Integer.parseInt(str), MAX_X); } catch (NumberFormatException nfe) {} setW(str);
}
/** @since 0.9 shorter parameter */
public void setW(String str) {
try {
_width = Math.max(MIN_X, Math.min(Integer.parseInt(str), MAX_X));
} catch (NumberFormatException nfe) {}
} }
public void setRefreshDelay(String str) { public void setRefreshDelay(String str) {
@@ -89,6 +123,14 @@ public class GraphHelper extends FormHandler {
/** @since 0.8.7 */ /** @since 0.8.7 */
public void setPersistent(String foo) { _persistent = true; } public void setPersistent(String foo) { _persistent = true; }
/**
* For single stat page
* @since 0.9
*/
public void setStat(String stat) {
_stat = stat;
}
public String getImages() { public String getImages() {
if (StatSummarizer.isDisabled()) if (StatSummarizer.isDisabled())
@@ -109,11 +151,11 @@ public class GraphHelper extends FormHandler {
} }
if (hasTx && hasRx && !_showEvents) { if (hasTx && hasRx && !_showEvents) {
_out.write("<a href=\"viewstat.jsp?stat=bw.combined" _out.write("<a href=\"viewstat?stat=bw.combined"
+ "&amp;periodCount=" + (3 * _periodCount ) + "&amp;periodCount=" + (3 * _periodCount )
+ "&amp;width=" + (3 * _width) + "&amp;width=" + (3 * _width)
+ "&amp;height=" + (3 * _height) + "&amp;height=" + (3 * _height)
+ "\" target=\"_blank\">"); + "\">");
String title = _("Combined bandwidth graph"); String title = _("Combined bandwidth graph");
_out.write("<img class=\"statimage\"" _out.write("<img class=\"statimage\""
+ " src=\"viewstat.jsp?stat=bw.combined" + " src=\"viewstat.jsp?stat=bw.combined"
@@ -128,14 +170,14 @@ public class GraphHelper extends FormHandler {
Rate r = lsnr.getRate(); Rate r = lsnr.getRate();
// e.g. "statname for 60m" // e.g. "statname for 60m"
String title = _("{0} for {1}", r.getRateStat().getName(), DataHelper.formatDuration2(_periodCount * r.getPeriod())); String title = _("{0} for {1}", r.getRateStat().getName(), DataHelper.formatDuration2(_periodCount * r.getPeriod()));
_out.write("<a href=\"viewstat.jsp?stat=" _out.write("<a href=\"graph?stat="
+ r.getRateStat().getName() + r.getRateStat().getName()
+ "&amp;showEvents=" + _showEvents + '.' + r.getPeriod()
+ "&amp;period=" + r.getPeriod() + "&amp;c=" + (3 * _periodCount)
+ "&amp;periodCount=" + (3 * _periodCount) + "&amp;w=" + (3 * _width)
+ "&amp;width=" + (3 * _width) + "&amp;h=" + (3 * _height)
+ "&amp;height=" + (3 * _height) + (_showEvents ? "&amp;showEvents=1" : "")
+ "\" target=\"_blank\">"); + "\">");
_out.write("<img class=\"statimage\" border=\"0\"" _out.write("<img class=\"statimage\" border=\"0\""
+ " src=\"viewstat.jsp?stat=" + " src=\"viewstat.jsp?stat="
+ r.getRateStat().getName() + r.getRateStat().getName()
@@ -156,6 +198,134 @@ public class GraphHelper extends FormHandler {
return ""; return "";
} }
/**
* For single stat page
* @since 0.9
*/
public String getSingleStat() {
try {
if (StatSummarizer.isDisabled())
return "";
if (_stat == null) {
_out.write("No stat");
return "";
}
List<Rate> rates = StatSummarizer.instance().parseSpecs(_stat);
if (rates.size() != 1) {
_out.write("Graphs not enabled for " + _stat);
return "";
}
Rate r = rates.get(0);
_out.write("<h3>");
_out.write(_("{0} for {1}", r.getRateStat().getName(), DataHelper.formatDuration2(_periodCount * r.getPeriod())));
if (_end > 0)
_out.write(' ' + _("ending {0} ago", DataHelper.formatDuration2(_end * r.getPeriod())));
_out.write("</h3><p><img class=\"statimage\" border=\"0\""
+ " src=\"viewstat.jsp?stat="
+ r.getRateStat().getName()
+ "&amp;showEvents=" + _showEvents
+ "&amp;period=" + r.getPeriod()
+ "&amp;periodCount=" + _periodCount
+ "&amp;end=" + _end
+ "&amp;width=" + _width
+ "&amp;height=" + _height
+ "\"></p><p>\n");
if (_width < MAX_X && _height < MAX_Y) {
_out.write(link(_stat, _showEvents, _periodCount, _end, _width * 3 / 2, _height * 3 / 2));
_out.write(_("Larger"));
_out.write("</a> - ");
}
if (_width > MIN_X && _height > MIN_Y) {
_out.write(link(_stat, _showEvents, _periodCount, _end, _width * 2 / 3, _height * 2 / 3));
_out.write(_("Smaller"));
_out.write("</a> - ");
}
if (_height < MAX_Y) {
_out.write(link(_stat, _showEvents, _periodCount, _end, _width, _height * 3 / 2));
_out.write(_("Taller"));
_out.write("</a> - ");
}
if (_height > MIN_Y) {
_out.write(link(_stat, _showEvents, _periodCount, _end, _width, _height * 2 / 3));
_out.write(_("Shorter"));
_out.write("</a> - ");
}
if (_width < MAX_X) {
_out.write(link(_stat, _showEvents, _periodCount, _end, _width * 3 / 2, _height));
_out.write(_("Wider"));
_out.write("</a> - ");
}
if (_width > MIN_X) {
_out.write(link(_stat, _showEvents, _periodCount, _end, _width * 2 / 3, _height));
_out.write(_("Narrower"));
_out.write("</a>");
}
_out.write("<br>");
if (_periodCount < MAX_C) {
_out.write(link(_stat, _showEvents, _periodCount * 2, _end, _width, _height));
_out.write(_("Larger interval"));
_out.write("</a> - ");
}
if (_periodCount > MIN_C) {
_out.write(link(_stat, _showEvents, _periodCount / 2, _end, _width, _height));
_out.write(_("Smaller interval"));
_out.write("</a>");
}
_out.write("<br>");
if (_periodCount < MAX_C) {
_out.write(link(_stat, _showEvents, _periodCount, _end + _periodCount, _width, _height));
_out.write(_("Previous interval"));
_out.write("</a>");
}
if (_end > 0) {
int end = _end - _periodCount;
if (end <= 0)
end = 0;
if (_periodCount < MAX_C)
_out.write(" - ");
_out.write(link(_stat, _showEvents, _periodCount, end, _width, _height));
_out.write(_("Next interval"));
_out.write("</a> ");
}
_out.write("<br>");
_out.write(link(_stat, !_showEvents, _periodCount, _end, _width, _height));
_out.write(_showEvents ? _("Plot averages") : _("plot events"));
_out.write("</a>");
_out.write("</p><p><i>" + _("All times are UTC.") + "</i></p>\n");
} catch (IOException ioe) {
ioe.printStackTrace();
}
return "";
}
/** @since 0.9 */
private static String link(String stat, boolean showEvents,
int periodCount, int end,
int width, int height) {
return
"<a href=\"graph?stat="
+ stat
+ "&amp;c=" + periodCount
+ "&amp;w=" + width
+ "&amp;h=" + height
+ (end > 0 ? "&amp;e=" + end : "")
+ (showEvents ? "&amp;showEvents=1" : "")
+ "\">";
}
private static final int[] times = { 60, 2*60, 5*60, 10*60, 30*60, 60*60, -1 }; private static final int[] times = { 60, 2*60, 5*60, 10*60, 30*60, 60*60, -1 };
public String getForm() { public String getForm() {
@@ -171,8 +341,8 @@ public class GraphHelper extends FormHandler {
"<input type=\"hidden\" name=\"action\" value=\"foo\">\n" + "<input type=\"hidden\" name=\"action\" value=\"foo\">\n" +
"<input type=\"hidden\" name=\"nonce\" value=\"" + nonce + "\" >\n"); "<input type=\"hidden\" name=\"nonce\" value=\"" + nonce + "\" >\n");
_out.write(_("Periods") + ": <input size=\"5\" style=\"text-align: right;\" type=\"text\" name=\"periodCount\" value=\"" + _periodCount + "\"><br>\n"); _out.write(_("Periods") + ": <input size=\"5\" style=\"text-align: right;\" type=\"text\" name=\"periodCount\" value=\"" + _periodCount + "\"><br>\n");
_out.write(_("Plot averages") + ": <input type=\"radio\" class=\"optbox\" name=\"showEvents\" value=\"false\" " + (_showEvents ? "" : "checked=\"true\" ") + "> "); _out.write(_("Plot averages") + ": <input type=\"radio\" class=\"optbox\" name=\"showEvents\" value=\"false\" " + (_showEvents ? "" : "checked=\"checked\" ") + "> ");
_out.write(_("or")+ " " +_("plot events") + ": <input type=\"radio\" class=\"optbox\" name=\"showEvents\" value=\"true\" "+ (_showEvents ? "checked=\"true\" " : "") + "><br>\n"); _out.write(_("or")+ " " +_("plot events") + ": <input type=\"radio\" class=\"optbox\" name=\"showEvents\" value=\"true\" "+ (_showEvents ? "checked=\"checked\" " : "") + "><br>\n");
_out.write(_("Image sizes") + ": " + _("width") + ": <input size=\"4\" style=\"text-align: right;\" type=\"text\" name=\"width\" value=\"" + _width _out.write(_("Image sizes") + ": " + _("width") + ": <input size=\"4\" style=\"text-align: right;\" type=\"text\" name=\"width\" value=\"" + _width
+ "\"> " + _("pixels") + ", " + _("height") + ": <input size=\"4\" style=\"text-align: right;\" type=\"text\" name=\"height\" value=\"" + _height + "\"> " + _("pixels") + ", " + _("height") + ": <input size=\"4\" style=\"text-align: right;\" type=\"text\" name=\"height\" value=\"" + _height
+ "\"> " + _("pixels") + "<br>\n"); + "\"> " + _("pixels") + "<br>\n");
@@ -182,7 +352,7 @@ public class GraphHelper extends FormHandler {
_out.write(Integer.toString(times[i])); _out.write(Integer.toString(times[i]));
_out.write("\""); _out.write("\"");
if (times[i] == _refreshDelaySeconds) if (times[i] == _refreshDelaySeconds)
_out.write(" selected=\"true\""); _out.write(" selected=\"selected\"");
_out.write(">"); _out.write(">");
if (times[i] > 0) if (times[i] > 0)
_out.write(DataHelper.formatDuration2(times[i] * 1000)); _out.write(DataHelper.formatDuration2(times[i] * 1000));
@@ -195,7 +365,7 @@ public class GraphHelper extends FormHandler {
" <input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"persistent\""); " <input type=\"checkbox\" class=\"optbox\" value=\"true\" name=\"persistent\"");
boolean persistent = _context.getBooleanPropertyDefaultTrue(SummaryListener.PROP_PERSISTENT); boolean persistent = _context.getBooleanPropertyDefaultTrue(SummaryListener.PROP_PERSISTENT);
if (persistent) if (persistent)
_out.write(" checked=\"true\""); _out.write(" checked=\"checked\"");
_out.write(">" + _out.write(">" +
"<hr><div class=\"formaction\"><input type=\"submit\" class=\"acceot\" value=\"" + _("Save settings and redraw graphs") + "\"></div></form>"); "<hr><div class=\"formaction\"><input type=\"submit\" class=\"acceot\" value=\"" + _("Save settings and redraw graphs") + "\"></div></form>");
} catch (IOException ioe) { } catch (IOException ioe) {

View File

@@ -33,6 +33,7 @@ import net.i2p.data.RouterInfo;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
import net.i2p.router.TunnelPoolSettings; import net.i2p.router.TunnelPoolSettings;
import net.i2p.router.networkdb.kademlia.HashDistance; // debug import net.i2p.router.networkdb.kademlia.HashDistance; // debug
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
import net.i2p.util.HexDump; // debug import net.i2p.util.HexDump; // debug
import net.i2p.util.ObjectCounter; import net.i2p.util.ObjectCounter;
import net.i2p.util.OrderedProperties; import net.i2p.util.OrderedProperties;
@@ -105,7 +106,10 @@ public class NetDbRenderer {
StringBuilder buf = new StringBuilder(4*1024); StringBuilder buf = new StringBuilder(4*1024);
buf.append("<h2>" + _("Network Database Contents") + "</h2>\n"); buf.append("<h2>" + _("Network Database Contents") + "</h2>\n");
buf.append("<a href=\"netdb\">" + _("View RouterInfo") + "</a>"); buf.append("<a href=\"netdb\">" + _("View RouterInfo") + "</a>");
buf.append("<h3>").append(_("LeaseSets")).append("</h3>\n"); buf.append("<h3>").append(_("LeaseSets"));
if (debug)
buf.append(" - Debug mode - Sorted by hash distance, closest first");
buf.append("</h3>\n");
Hash ourRKey; Hash ourRKey;
Set<LeaseSet> leases; Set<LeaseSet> leases;
DecimalFormat fmt; DecimalFormat fmt;
@@ -120,15 +124,16 @@ public class NetDbRenderer {
} }
leases.addAll(_context.netDb().getLeases()); leases.addAll(_context.netDb().getLeases());
int medianCount = 0; int medianCount = 0;
int rapCount = 0;
BigInteger median = null; BigInteger median = null;
int c = 0; int c = 0;
if (debug) { if (debug) {
// Find the center of the RAP leasesets // Find the center of the RAP leasesets
for (LeaseSet ls : leases) { for (LeaseSet ls : leases) {
if (ls.getReceivedAsPublished()) if (ls.getReceivedAsPublished())
medianCount++; rapCount++;
} }
medianCount /= 2; medianCount = rapCount / 2;
} }
long now = _context.clock().now(); long now = _context.clock().now();
for (LeaseSet ls : leases) { for (LeaseSet ls : leases) {
@@ -167,9 +172,10 @@ public class NetDbRenderer {
if (c++ == medianCount) if (c++ == medianCount)
median = dist; median = dist;
} }
buf.append(" Dist: <b>").append(fmt.format(biLog2(dist))).append("</b>"); buf.append(" Dist: <b>").append(fmt.format(biLog2(dist))).append("</b><br>");
buf.append(" RKey: ").append(ls.getRoutingKey().toBase64()); buf.append("Routing Key: ").append(ls.getRoutingKey().toBase64());
buf.append("<br>"); buf.append("<br>");
buf.append("Encryption Key: ").append(ls.getEncryptionKey().toBase64().substring(0, 20)).append("...<br>");
} }
for (int i = 0; i < ls.getLeaseCount(); i++) { for (int i = 0; i < ls.getLeaseCount(); i++) {
buf.append(_("Lease")).append(' ').append(i + 1).append(": " + _("Gateway") + ' '); buf.append(_("Lease")).append(' ').append(i + 1).append(": " + _("Gateway") + ' ');
@@ -181,18 +187,23 @@ public class NetDbRenderer {
buf.setLength(0); buf.setLength(0);
} }
if (debug) { if (debug) {
buf.append("<p><b>Total Leasesets: " + leases.size()); FloodfillNetworkDatabaseFacade netdb = (FloodfillNetworkDatabaseFacade)_context.netDb();
buf.append("</b></p><p><b>Published (RAP) Leasesets: " + _context.netDb().getKnownLeaseSets()); buf.append("<p><b>Total Leasesets: ").append(leases.size());
buf.append("</b></p><p><b>Published (RAP) Leasesets: ").append(netdb.getKnownLeaseSets());
//buf.append("</b></p><p><b>Mod Data: " + HexDump.dump(_context.routingKeyGenerator().getModData())); //buf.append("</b></p><p><b>Mod Data: " + HexDump.dump(_context.routingKeyGenerator().getModData()));
int ff = _context.peerManager().getPeersByCapability(FloodfillNetworkDatabaseFacade.CAPABILITY_FLOODFILL).size();
buf.append("</b></p><p><b>Known Floodfills: ").append(ff);
buf.append("</b></p><p><b>Currently Floodfill? ");
buf.append(netdb.floodfillEnabled() ? "yes" : "no");
buf.append("</b></p><p><b>Network data (only valid if floodfill):"); buf.append("</b></p><p><b>Network data (only valid if floodfill):");
//buf.append("</b></p><p><b>Center of Key Space (router hash): " + ourRKey.toBase64()); //buf.append("</b></p><p><b>Center of Key Space (router hash): " + ourRKey.toBase64());
if (median != null) { if (median != null) {
double log2 = biLog2(median); double log2 = biLog2(median);
buf.append("</b></p><p><b>Median distance (bits): " + fmt.format(log2)); buf.append("</b></p><p><b>Median distance (bits): ").append(fmt.format(log2));
// 3 for 8 floodfills... -1 for median // 3 for 8 floodfills... -1 for median
int total = (int) Math.round(Math.pow(2, 3 + 256 - 1 - log2)); int total = (int) Math.round(Math.pow(2, 3 + 256 - 1 - log2));
buf.append("</b></p><p><b>Estimated total floodfills: " + total); buf.append("</b></p><p><b>Estimated total floodfills: ").append(total);
buf.append("</b></p><p><b>Estimated network total leasesets: " + (total * leases.size() / 8)); buf.append("</b></p><p><b>Estimated total leasesets: ").append(total * rapCount / 8);
} }
buf.append("</b></p>"); buf.append("</b></p>");
} }

View File

@@ -172,32 +172,37 @@ public class StatSummarizer implements Runnable {
public boolean renderPng(Rate rate, OutputStream out) throws IOException { public boolean renderPng(Rate rate, OutputStream out) throws IOException {
return renderPng(rate, out, GraphHelper.DEFAULT_X, GraphHelper.DEFAULT_Y, return renderPng(rate, out, GraphHelper.DEFAULT_X, GraphHelper.DEFAULT_Y,
false, false, false, false, -1, true); false, false, false, false, -1, 0, true);
} }
/** /**
* This does the single data graphs. * This does the single data graphs.
* For the two-data bandwidth graph see renderRatePng(). * For the two-data bandwidth graph see renderRatePng().
* Synchronized to conserve memory. * Synchronized to conserve memory.
*
* @param end number of periods before now
* @return success * @return success
*/ */
public boolean renderPng(Rate rate, OutputStream out, int width, int height, boolean hideLegend, public boolean renderPng(Rate rate, OutputStream out, int width, int height, boolean hideLegend,
boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount,
boolean showCredit) throws IOException { int end, boolean showCredit) throws IOException {
try { try {
try { try {
_sem.acquire(); _sem.acquire();
} catch (InterruptedException ie) {} } catch (InterruptedException ie) {}
return locked_renderPng(rate, out, width, height, hideLegend, hideGrid, hideTitle, showEvents, return locked_renderPng(rate, out, width, height, hideLegend, hideGrid, hideTitle, showEvents,
periodCount, showCredit); periodCount, end, showCredit);
} finally { } finally {
_sem.release(); _sem.release();
} }
} }
/**
* @param end number of periods before now
*/
private boolean locked_renderPng(Rate rate, OutputStream out, int width, int height, boolean hideLegend, private boolean locked_renderPng(Rate rate, OutputStream out, int width, int height, boolean hideLegend,
boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount,
boolean showCredit) throws IOException { int end, boolean showCredit) throws IOException {
if (width > GraphHelper.MAX_X) if (width > GraphHelper.MAX_X)
width = GraphHelper.MAX_X; width = GraphHelper.MAX_X;
else if (width <= 0) else if (width <= 0)
@@ -206,9 +211,11 @@ public class StatSummarizer implements Runnable {
height = GraphHelper.MAX_Y; height = GraphHelper.MAX_Y;
else if (height <= 0) else if (height <= 0)
height = GraphHelper.DEFAULT_Y; height = GraphHelper.DEFAULT_Y;
if (end < 0)
end = 0;
for (SummaryListener lsnr : _listeners) { for (SummaryListener lsnr : _listeners) {
if (lsnr.getRate().equals(rate)) { if (lsnr.getRate().equals(rate)) {
lsnr.renderPng(out, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount, showCredit); lsnr.renderPng(out, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount, end, showCredit);
return true; return true;
} }
} }
@@ -368,7 +375,7 @@ public class StatSummarizer implements Runnable {
* @param specs statName.period,statName.period,statName.period * @param specs statName.period,statName.period,statName.period
* @return list of Rate objects * @return list of Rate objects
*/ */
private List<Rate> parseSpecs(String specs) { List<Rate> parseSpecs(String specs) {
StringTokenizer tok = new StringTokenizer(specs, ","); StringTokenizer tok = new StringTokenizer(specs, ",");
List<Rate> rv = new ArrayList(); List<Rate> rv = new ArrayList();
while (tok.hasMoreTokens()) { while (tok.hasMoreTokens()) {

View File

@@ -55,7 +55,7 @@ class SummaryListener implements RateSummaryListener {
static final int PERIODS = 60 * 24; // 1440 static final int PERIODS = 60 * 24; // 1440
private static final int MIN_ROWS = PERIODS; private static final int MIN_ROWS = PERIODS;
private static final int MAX_ROWS = 91 * MIN_ROWS; static final int MAX_ROWS = 91 * MIN_ROWS;
private static final long THREE_MONTHS = 91l * 24 * 60 * 60 * 1000; private static final long THREE_MONTHS = 91l * 24 * 60 * 60 * 1000;
public SummaryListener(Rate r) { public SummaryListener(Rate r) {
@@ -191,10 +191,15 @@ class SummaryListener implements RateSummaryListener {
_db = null; _db = null;
} }
public void renderPng(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, boolean showCredit) throws IOException { /**
* @param end number of periods before now
*/
public void renderPng(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid,
boolean hideTitle, boolean showEvents, int periodCount,
int end, boolean showCredit) throws IOException {
if (_renderer == null || _db == null) if (_renderer == null || _db == null)
throw new IOException("No RRD, check logs for previous errors"); throw new IOException("No RRD, check logs for previous errors");
_renderer.render(out, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount, showCredit); _renderer.render(out, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount, end, showCredit);
} }
public void renderPng(OutputStream out) throws IOException { public void renderPng(OutputStream out) throws IOException {

View File

@@ -81,13 +81,21 @@ class SummaryRenderer {
} }
public void render(OutputStream out) throws IOException { render(out, GraphHelper.DEFAULT_X, GraphHelper.DEFAULT_Y, public void render(OutputStream out) throws IOException { render(out, GraphHelper.DEFAULT_X, GraphHelper.DEFAULT_Y,
false, false, false, false, -1, false); } false, false, false, false, -1, 0, false); }
public void render(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, boolean showCredit) throws IOException { /**
* @param endp number of periods before now
*/
public void render(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid,
boolean hideTitle, boolean showEvents, int periodCount,
int endp, boolean showCredit) throws IOException {
long end = _listener.now() - 75*1000; long end = _listener.now() - 75*1000;
long period = _listener.getRate().getPeriod();
if (endp > 0)
end -= period * endp;
if (periodCount <= 0 || periodCount > _listener.getRows()) if (periodCount <= 0 || periodCount > _listener.getRows())
periodCount = _listener.getRows(); periodCount = _listener.getRows();
long start = end - _listener.getRate().getPeriod()*periodCount; long start = end - (period * periodCount);
//long begin = System.currentTimeMillis(); //long begin = System.currentTimeMillis();
try { try {
RrdGraphDef def = new RrdGraphDef(); RrdGraphDef def = new RrdGraphDef();
@@ -103,9 +111,9 @@ class SummaryRenderer {
String p; String p;
// we want the formatting and translation of formatDuration2(), except not zh, and not the &nbsp; // we want the formatting and translation of formatDuration2(), except not zh, and not the &nbsp;
if (IS_WIN && "zh".equals(Messages.getLanguage(_context))) if (IS_WIN && "zh".equals(Messages.getLanguage(_context)))
p = DataHelper.formatDuration(_listener.getRate().getPeriod()); p = DataHelper.formatDuration(period);
else else
p = DataHelper.formatDuration2(_listener.getRate().getPeriod()).replace("&nbsp;", " "); p = DataHelper.formatDuration2(period).replace("&nbsp;", " ");
if (showEvents) if (showEvents)
title = name + ' ' + _("events in {0}", p); title = name + ' ' + _("events in {0}", p);
else else

View File

@@ -134,6 +134,7 @@ public class TunnelRenderer {
out.write("<div class=\"statusnotes\"><b>" + _("Inactive participating tunnels") + ": " + inactive + "</b></div>\n"); out.write("<div class=\"statusnotes\"><b>" + _("Inactive participating tunnels") + ": " + inactive + "</b></div>\n");
out.write("<div class=\"statusnotes\"><b>" + _("Lifetime bandwidth usage") + ": " + DataHelper.formatSize2(processed*1024) + "B</b></div>\n"); out.write("<div class=\"statusnotes\"><b>" + _("Lifetime bandwidth usage") + ": " + DataHelper.formatSize2(processed*1024) + "B</b></div>\n");
//renderPeers(out); //renderPeers(out);
out.write("</div>");
} }
private static class TunnelComparator implements Comparator<HopConfig> { private static class TunnelComparator implements Comparator<HopConfig> {
@@ -222,7 +223,7 @@ public class TunnelRenderer {
} }
} }
if (live <= 0) if (live <= 0)
out.write("<div class=\"statusnotes\"><center><b>" + _("No tunnels; waiting for the grace period to end.") + "</center></b></div>\n"); out.write("<div class=\"statusnotes\"><center><b>" + _("No tunnels; waiting for the grace period to end.") + "</b></center></div>\n");
out.write("<div class=\"statusnotes\"><center><b>" + _("Lifetime bandwidth usage") + ": " + out.write("<div class=\"statusnotes\"><center><b>" + _("Lifetime bandwidth usage") + ": " +
DataHelper.formatSize2(processedIn*1024) + "B " + _("in") + ", " + DataHelper.formatSize2(processedIn*1024) + "B " + _("in") + ", " +
DataHelper.formatSize2(processedOut*1024) + "B " + _("out") + "</b></center></div>"); DataHelper.formatSize2(processedOut*1024) + "B " + _("out") + "</b></center></div>");

View File

@@ -41,7 +41,7 @@
<input type="radio" class="optbox" name="mode" value="2" <%=reseedHelper.modeChecked(2) %> > <input type="radio" class="optbox" name="mode" value="2" <%=reseedHelper.modeChecked(2) %> >
<b><%=intl._("Use non-SSL only")%></b></td></tr> <b><%=intl._("Use non-SSL only")%></b></td></tr>
<tr><td class="mediumtags" align="right"><b><%=intl._("Reseed URLs")%>:</b></td> <tr><td class="mediumtags" align="right"><b><%=intl._("Reseed URLs")%>:</b></td>
<td><textarea name="reseedURL" wrap="off" spellcheck="false"><jsp:getProperty name="reseedHelper" property="reseedURL" /></textarea></td></tr> <td><textarea wrap="off" name="reseedURL" cols="60" rows="7" spellcheck="false"><jsp:getProperty name="reseedHelper" property="reseedURL" /></textarea></td></tr>
<tr><td class="mediumtags" align="right"><b><%=intl._("Enable HTTP Proxy?")%></b></td> <tr><td class="mediumtags" align="right"><b><%=intl._("Enable HTTP Proxy?")%></b></td>
<td><input type="checkbox" class="optbox" name="enable" value="true" <jsp:getProperty name="reseedHelper" property="enable" /> ></td></tr> <td><input type="checkbox" class="optbox" name="enable" value="true" <jsp:getProperty name="reseedHelper" property="enable" /> ></td></tr>

View File

@@ -76,10 +76,21 @@ function toggleAll(category)
<h3><%=intl._("Configure I2P Stat Collection")%></h3> <h3><%=intl._("Configure I2P Stat Collection")%></h3>
<p><%=intl._("Enable full stats?")%> <p><%=intl._("Enable full stats?")%>
<input type="checkbox" class="optbox" name="isFull" value="true" <% <input type="checkbox" class="optbox" name="isFull" value="true" <%
if (statshelper.getIsFull()) { %>checked="true" <% } %> > if (statshelper.getIsFull()) { %>checked="checked" <% } %> >
(<%=intl._("change requires restart to take effect")%>)<br> (<%=intl._("change requires restart to take effect")%>)<br>
<%=intl._("Stat file")%>: <input type="text" name="filename" value="<%=statshelper.getFilename()%>" ><br> <%
<%=intl._("Filter")%>: (<a href="javascript:void(null);" onclick="toggleAll('*')"><%=intl._("toggle all")%></a>)<br></p>
// stats.log for devs only and grows without bounds, not recommended
boolean shouldShowLog = statshelper.shouldShowLog();
if (shouldShowLog) {
%><%=intl._("Stat file")%>: <input type="text" name="filename" value="<%=statshelper.getFilename()%>" ><br>
Warning - Log with care, stat file grows without limit.<br>
<%
} // shouldShowLog
%><%=intl._("Filter")%>: (<a href="javascript:void(null);" onclick="toggleAll('*')"><%=intl._("toggle all")%></a>)<br></p>
<div class="wideload"> <div class="wideload">
<table> <table>
<% while (statshelper.hasMoreStats()) { <% while (statshelper.hasMoreStats()) {
@@ -90,27 +101,51 @@ function toggleAll(category)
(<a href="javascript:void(null);" onclick="toggleAll('<%=statshelper.getCurrentGroupName()%>')"><%=intl._("toggle all")%></a>) (<a href="javascript:void(null);" onclick="toggleAll('<%=statshelper.getCurrentGroupName()%>')"><%=intl._("toggle all")%></a>)
</td></tr> </td></tr>
<tr class="tablefooter"> <tr class="tablefooter">
<td align="center"><b><%=intl._("Log")%></b></td> <%
<td align="center"><b><%=intl._("Graph")%></b></td>
if (shouldShowLog) {
%> <td align="center"><b><%=intl._("Log")%></b></td>
<%
} // shouldShowLog
%> <td align="center"><b><%=intl._("Graph")%></b></td>
<td></td></tr> <td></td></tr>
<% <%
} // end iterating over required groups for the current stat %> } // end iterating over required groups for the current stat %>
<tr><td align="center"> <tr>
<%
if (shouldShowLog) {
%> <td align="center">
<a name="<%=statshelper.getCurrentStatName()%>"></a> <a name="<%=statshelper.getCurrentStatName()%>"></a>
<input type="checkbox" class="optbox <%=statshelper.getCurrentGroupName()%>" name="statList" value="<%=statshelper.getCurrentStatName()%>" <% <input type="checkbox" class="optbox <%=statshelper.getCurrentGroupName()%>" name="statList" value="<%=statshelper.getCurrentStatName()%>" <%
if (statshelper.getCurrentIsLogged()) { %>checked="true" <% } %> ></td> if (statshelper.getCurrentIsLogged()) { %>checked="checked" <% } %> ></td>
<td align="center"> <%
} // shouldShowLog
%> <td align="center">
<% if (statshelper.getCurrentCanBeGraphed()) { %> <% if (statshelper.getCurrentCanBeGraphed()) { %>
<input type="checkbox" class="optbox <%=statshelper.getCurrentGroupName()%>" name="graphList" value="<%=statshelper.getCurrentGraphName()%>" <% <input type="checkbox" class="optbox <%=statshelper.getCurrentGroupName()%>" name="graphList" value="<%=statshelper.getCurrentGraphName()%>" <%
if (statshelper.getCurrentIsGraphed()) { %>checked="true" <% } %> ><% } %></td> if (statshelper.getCurrentIsGraphed()) { %>checked="checked" <% } %> ><% } %></td>
<td align="left"><b><%=statshelper.getCurrentStatName()%>:</b><br> <td align="left"><b><%=statshelper.getCurrentStatName()%>:</b><br>
<%=statshelper.getCurrentStatDescription()%></td></tr><% <%=statshelper.getCurrentStatDescription()%></td></tr><%
} // end iterating over all stats %> } // end iterating over all stats
<tr><td colspan="3"></td></tr>
if (shouldShowLog) {
%> <tr><td colspan="3"></td></tr>
<tr><td align="center"><input type="checkbox" class="optbox" name="explicitFilter" ></td> <tr><td align="center"><input type="checkbox" class="optbox" name="explicitFilter" ></td>
<td colspan="2"><%=intl._("Advanced filter")%>: <td colspan="2"><%=intl._("Advanced filter")%>:
<input type="text" name="explicitFilterValue" value="<%=statshelper.getExplicitFilter()%>" size="40" ></td></tr> <input type="text" name="explicitFilterValue" value="<%=statshelper.getExplicitFilter()%>" size="40" ></td></tr>
<tr class="tablefooter"><td colspan="3" align="right"> <%
} // shouldShowLog
%> <tr class="tablefooter"><td colspan="3" align="right">
<input type="reset" class="cancel" value="<%=intl._("Cancel")%>" > <input type="reset" class="cancel" value="<%=intl._("Cancel")%>" >
<input type="submit" name="shouldsave" class="accept" value="<%=intl._("Save changes")%>" > <input type="submit" name="shouldsave" class="accept" value="<%=intl._("Save changes")%>" >
</td></tr> </td></tr>

View File

@@ -56,9 +56,9 @@
<td><input type="text" size="10" name="proxyPort" value="<jsp:getProperty name="updatehelper" property="proxyPort" />" /></td></tr> <td><input type="text" size="10" name="proxyPort" value="<jsp:getProperty name="updatehelper" property="proxyPort" />" /></td></tr>
<% if (updatehelper.canInstall()) { %> <% if (updatehelper.canInstall()) { %>
<tr><td class= "mediumtags" align="right"><b><%=intl._("Update URLs")%>:</b></td> <tr><td class= "mediumtags" align="right"><b><%=intl._("Update URLs")%>:</b></td>
<td><textarea name="updateURL" wrap="off" spellcheck="false"><jsp:getProperty name="updatehelper" property="updateURL" /></textarea></td> <td><textarea cols="60" rows="6" name="updateURL" wrap="off" spellcheck="false"><jsp:getProperty name="updatehelper" property="updateURL" /></textarea></td>
</tr><tr><td class= "mediumtags" align="right"><b><%=intl._("Trusted keys")%>:</b></td> </tr><tr><td class= "mediumtags" align="right"><b><%=intl._("Trusted keys")%>:</b></td>
<td><textarea name="trustedKeys" wrap="off" spellcheck="false"><jsp:getProperty name="updatehelper" property="trustedKeys" /></textarea></td> <td><textarea cols="60" rows="6" name="trustedKeys" wrap="off" spellcheck="false"><jsp:getProperty name="updatehelper" property="trustedKeys" /></textarea></td>
</tr><tr><td class= "mediumtags" align="right"><b><%=intl._("Update with unsigned development builds?")%></b></td> </tr><tr><td class= "mediumtags" align="right"><b><%=intl._("Update with unsigned development builds?")%></b></td>
<td><jsp:getProperty name="updatehelper" property="updateUnsigned" /></td> <td><jsp:getProperty name="updatehelper" property="updateUnsigned" /></td>
</tr><tr><td class= "mediumtags" align="right"><b><%=intl._("Unsigned Build URL")%>:</b></td> </tr><tr><td class= "mediumtags" align="right"><b><%=intl._("Unsigned Build URL")%>:</b></td>

View File

@@ -0,0 +1,22 @@
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<%@include file="css.jsi" %>
<%=intl.title("graphs")%>
<jsp:useBean class="net.i2p.router.web.GraphHelper" id="graphHelper" scope="request" />
<jsp:setProperty name="graphHelper" property="contextId" value="<%=(String)session.getAttribute(\"i2p.contextId\")%>" />
<% /* GraphHelper sets the defaults in setContextId, so setting the properties must be after the context */ %>
<jsp:setProperty name="graphHelper" property="*" />
<%
graphHelper.storeWriter(out);
%>
</head><body>
<%@include file="summary.jsi" %>
<h1><%=intl._("I2P Performance Graphs")%></h1>
<div class="main" id="main">
<div class="graphspanel">
<div class="widepanel">
<jsp:getProperty name="graphHelper" property="singleStat" />
</div></div></div></body></html>

View File

@@ -6,7 +6,6 @@
<%@include file="css.jsi" %> <%@include file="css.jsi" %>
<%=intl.title("graphs")%> <%=intl.title("graphs")%>
<jsp:useBean class="net.i2p.router.web.GraphHelper" id="graphHelper" scope="request" /> <jsp:useBean class="net.i2p.router.web.GraphHelper" id="graphHelper" scope="request" />
<% graphHelper.storeMethod(request.getMethod()); %>
<jsp:setProperty name="graphHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="graphHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<% /* GraphHelper sets the defaults in setContextId, so setting the properties must be after the context */ %> <% /* GraphHelper sets the defaults in setContextId, so setting the properties must be after the context */ %>
<jsp:setProperty name="graphHelper" property="*" /> <jsp:setProperty name="graphHelper" property="*" />

View File

@@ -0,0 +1,33 @@
function ajax(url, target, refresh) {
// native XMLHttpRequest object
if (window.XMLHttpRequest) {
req = new XMLHttpRequest();
req.onreadystatechange = function() {ajaxDone(url, target, refresh);};
req.open("GET", url, true);
req.send(null);
// IE/Windows ActiveX version
} else if (window.ActiveXObject) {
req = new ActiveXObject("Microsoft.XMLDOM");
if (req) {
req.onreadystatechange = function() {ajaxDone(target);};
req.open("GET", url, true);
req.send(null);
}
}
}
function ajaxDone(url, target, refresh) {
// only if req is "loaded"
if (req.readyState == 4) {
// only if "OK"
if (req.status == 200) {
results = req.responseText;
document.getElementById(target).innerHTML = results;
document.getElementById("lowersection").style.display="block";
} else {
document.getElementById(target).innerHTML="<b>Router is down</b>";
document.getElementById("lowersection").style.display="none";
}
setTimeout(function() {ajax(url, target, refresh);}, refresh);
}
}

View File

@@ -47,12 +47,15 @@ if ( !rendered && ((rs != null) || fakeBw) ) {
int width = -1; int width = -1;
int height = -1; int height = -1;
int periodCount = -1; int periodCount = -1;
int end = 0;
String str = request.getParameter("width"); String str = request.getParameter("width");
if (str != null) try { width = Integer.parseInt(str); } catch (NumberFormatException nfe) {} if (str != null) try { width = Integer.parseInt(str); } catch (NumberFormatException nfe) {}
str = request.getParameter("height"); str = request.getParameter("height");
if (str != null) try { height = Integer.parseInt(str); } catch (NumberFormatException nfe) {} if (str != null) try { height = Integer.parseInt(str); } catch (NumberFormatException nfe) {}
str = request.getParameter("periodCount"); str = request.getParameter("periodCount");
if (str != null) try { periodCount = Integer.parseInt(str); } catch (NumberFormatException nfe) {} if (str != null) try { periodCount = Integer.parseInt(str); } catch (NumberFormatException nfe) {}
str = request.getParameter("end");
if (str != null) try { end = Integer.parseInt(str); } catch (NumberFormatException nfe) {}
boolean hideLegend = Boolean.valueOf(""+request.getParameter("hideLegend")).booleanValue(); boolean hideLegend = Boolean.valueOf(""+request.getParameter("hideLegend")).booleanValue();
boolean hideGrid = Boolean.valueOf(""+request.getParameter("hideGrid")).booleanValue(); boolean hideGrid = Boolean.valueOf(""+request.getParameter("hideGrid")).booleanValue();
boolean hideTitle = Boolean.valueOf(""+request.getParameter("hideTitle")).booleanValue(); boolean hideTitle = Boolean.valueOf(""+request.getParameter("hideTitle")).booleanValue();
@@ -63,7 +66,7 @@ if ( !rendered && ((rs != null) || fakeBw) ) {
if (fakeBw) if (fakeBw)
rendered = net.i2p.router.web.StatSummarizer.instance().renderRatePng(cout, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount, showCredit); rendered = net.i2p.router.web.StatSummarizer.instance().renderRatePng(cout, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount, showCredit);
else else
rendered = net.i2p.router.web.StatSummarizer.instance().renderPng(rate, cout, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount, showCredit); rendered = net.i2p.router.web.StatSummarizer.instance().renderPng(rate, cout, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount, end, showCredit);
} }
if (rendered) if (rendered)
cout.close(); cout.close();

View File

@@ -72,6 +72,7 @@
<attribute name="Main-Class" value="net.i2p.sam.SAMBridge" /> <attribute name="Main-Class" value="net.i2p.sam.SAMBridge" />
<attribute name="Class-Path" value="i2p.jar mstreaming.jar streaming.jar" /> <attribute name="Class-Path" value="i2p.jar mstreaming.jar streaming.jar" />
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" />
@@ -87,6 +88,7 @@
<attribute name="Main-Class" value="net.i2p.sam.SAMBridge" /> <attribute name="Main-Class" value="net.i2p.sam.SAMBridge" />
<attribute name="Class-Path" value="i2p.jar mstreaming.jar streaming.jar" /> <attribute name="Class-Path" value="i2p.jar mstreaming.jar streaming.jar" />
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" />

View File

@@ -64,6 +64,7 @@
<jar destfile="./build/streaming.jar" basedir="./build/obj" includes="**/*.class" > <jar destfile="./build/streaming.jar" basedir="./build/obj" includes="**/*.class" >
<manifest> <manifest>
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" />

View File

@@ -161,8 +161,7 @@ class ConnectionManager {
// active++; // active++;
//} //}
if (locked_tooManyStreams()) { if (locked_tooManyStreams()) {
if (_log.shouldLog(Log.WARN)) _log.logAlways(Log.WARN, "Refusing connection since we have exceeded our max of "
_log.warn("Refusing connection since we have exceeded our max of "
+ _maxConcurrentStreams + " connections"); + _maxConcurrentStreams + " connections");
reject = true; reject = true;
} else { } else {
@@ -233,8 +232,7 @@ class ConnectionManager {
while (true) { while (true) {
long remaining = expiration - _context.clock().now(); long remaining = expiration - _context.clock().now();
if (remaining <= 0) { if (remaining <= 0) {
if (_log.shouldLog(Log.WARN)) _log.logAlways(Log.WARN, "Refusing to connect since we have exceeded our max of "
_log.warn("Refusing to connect since we have exceeded our max of "
+ _maxConcurrentStreams + " connections"); + _maxConcurrentStreams + " connections");
_numWaiting--; _numWaiting--;
return null; return null;
@@ -243,8 +241,7 @@ class ConnectionManager {
if (locked_tooManyStreams()) { if (locked_tooManyStreams()) {
// allow a full buffer of pending/waiting streams // allow a full buffer of pending/waiting streams
if (_numWaiting > _maxConcurrentStreams) { if (_numWaiting > _maxConcurrentStreams) {
if (_log.shouldLog(Log.WARN)) _log.logAlways(Log.WARN, "Refusing connection since we have exceeded our max of "
_log.warn("Refusing connection since we have exceeded our max of "
+ _maxConcurrentStreams + " and there are " + _numWaiting + _maxConcurrentStreams + " and there are " + _numWaiting
+ " waiting already"); + " waiting already");
_numWaiting--; _numWaiting--;

View File

@@ -98,6 +98,7 @@
</fileset> </fileset>
<manifest> <manifest>
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" />

View File

@@ -170,6 +170,15 @@ public class AddressBean
return Base32.encode(hash) + ".b32.i2p"; return Base32.encode(hash) + ".b32.i2p";
} }
/** @since 0.9 */
public String getB64()
{
byte[] dest = Base64.decode(destination);
if (dest == null)
return "";
return I2PAppContext.getGlobalContext().sha().calculateHash(dest).toBase64();
}
/** @since 0.8.7 */ /** @since 0.8.7 */
public void setProperties(Properties p) { public void setProperties(Properties p) {
props = p; props = p;

View File

@@ -42,7 +42,7 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html> <html>
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>${book.book} <%=intl._("address book")%> - susidns</title> <title>${book.book} <%=intl._("address book")%> - susidns</title>
<link rel="stylesheet" type="text/css" href="css.css"> <link rel="stylesheet" type="text/css" href="css.css">
</head> </head>
@@ -158,7 +158,7 @@ ${book.loadBookMessages}
</td><td class="names"> </td><td class="names">
<span class="addrhlpr"><a href="details?h=${addr.name}" title="<%=intl._("More information on this entry")%>"><%=intl._("details")%></a></span> <span class="addrhlpr"><a href="details?h=${addr.name}" title="<%=intl._("More information on this entry")%>"><%=intl._("details")%></a></span>
</td> </td>
<td class="destinations"><textarea rows="1" style="height: 3em;" cols="40" wrap="off" readonly="readonly" name="dest_${addr.name}" >${addr.destination}</textarea></td> <td class="destinations"><textarea rows="1" style="height:3em;" wrap="off" cols="40" readonly="readonly" name="dest_${addr.name}" >${addr.destination}</textarea></td>
</tr> </tr>
</c:forEach> </c:forEach>
</table> </table>
@@ -190,7 +190,7 @@ ${book.loadBookMessages}
<table><tr><td> <table><tr><td>
<b><%=intl._("Host Name")%></b></td><td><input type="text" name="hostname" value="${book.hostname}" size="54"> <b><%=intl._("Host Name")%></b></td><td><input type="text" name="hostname" value="${book.hostname}" size="54">
</td></tr><tr><td> </td></tr><tr><td>
<b><%=intl._("Destination")%></b></td><td><textarea name="destination" rows="1" style="height: 3em;" cols="70" wrap="off" spellcheck="false">${book.destination}</textarea> <b><%=intl._("Destination")%></b></td><td><textarea name="destination" rows="1" style="height:3em" wrap="off" cols="70" spellcheck="false">${book.destination}</textarea>
</td></tr></table> </td></tr></table>
<p class="buttons"> <p class="buttons">
<input class="cancel" type="reset" value="<%=intl._("Cancel")%>" > <input class="cancel" type="reset" value="<%=intl._("Cancel")%>" >

View File

@@ -38,7 +38,7 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html> <html>
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=intl._("configuration")%> - susidns</title> <title><%=intl._("configuration")%> - susidns</title>
<link rel="stylesheet" type="text/css" href="css.css"> <link rel="stylesheet" type="text/css" href="css.css">
</head> </head>

View File

@@ -36,7 +36,7 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html> <html>
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>${book.book} <%=intl._("addressbook")%> - susidns</title> <title>${book.book} <%=intl._("addressbook")%> - susidns</title>
<link rel="stylesheet" type="text/css" href="css.css"> <link rel="stylesheet" type="text/css" href="css.css">
</head> </head>
@@ -94,6 +94,9 @@
<td><%=intl._("Base 32 Address")%></td> <td><%=intl._("Base 32 Address")%></td>
<td><a href="http://<%=b32%>/"><%=b32%></a></td> <td><a href="http://<%=b32%>/"><%=b32%></a></td>
</tr><tr class="list${book.trClass}"> </tr><tr class="list${book.trClass}">
<td><%=intl._("Base 64 Hash")%></td>
<td><%=addr.getB64()%></td>
</tr><tr class="list${book.trClass}">
<td><%=intl._("Address Helper")%></td> <td><%=intl._("Address Helper")%></td>
<td><a href="http://<%=addr.getName()%>/?i2paddresshelper=<%=addr.getDestination()%>"><%=intl._("link")%></a></td> <td><a href="http://<%=addr.getName()%>/?i2paddresshelper=<%=addr.getDestination()%>"><%=intl._("link")%></a></td>
</tr><tr class="list${book.trClass}"> </tr><tr class="list${book.trClass}">
@@ -119,7 +122,7 @@
<td><%=addr.getNotes()%></td> <td><%=addr.getNotes()%></td>
</tr><tr class="list${book.trClass}"> </tr><tr class="list${book.trClass}">
<td><%=intl._("Destination")%></td> <td><%=intl._("Destination")%></td>
<td class="destinations"><textarea rows="1" style="height: 3em;" cols="70" wrap="off" readonly="readonly" ><%=addr.getDestination()%></textarea></td> <td class="destinations"><textarea rows="1" style="height:3em;" wrap="off" cols="70" readonly="readonly" ><%=addr.getDestination()%></textarea></td>
</tr></table> </tr></table>
</div> </div>
<div id="buttons"> <div id="buttons">

View File

@@ -38,7 +38,7 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html> <html>
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=intl._("subscriptions")%> - susidns</title> <title><%=intl._("subscriptions")%> - susidns</title>
<link rel="stylesheet" type="text/css" href="css.css"> <link rel="stylesheet" type="text/css" href="css.css">
</head> </head>

View File

@@ -46,6 +46,7 @@
basedir="src/" excludes="WEB-INF/web.xml LICENSE src/**/*"> basedir="src/" excludes="WEB-INF/web.xml LICENSE src/**/*">
<manifest> <manifest>
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" />

View File

@@ -45,6 +45,7 @@
<attribute name="Main-Class" value="net.i2p.apps.systray.SysTray" /> <attribute name="Main-Class" value="net.i2p.apps.systray.SysTray" />
<attribute name="Class-Path" value="systray4j.jar" /> <attribute name="Class-Path" value="systray4j.jar" />
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" />

View File

@@ -16,3 +16,5 @@ wrapperdocs.url=http://wrapper.tanukisoftware.com/jdoc/
# these are only for unit test javadocs # these are only for unit test javadocs
i2pdocs.url=http://docs.i2p-projekt.de/javadoc/ i2pdocs.url=http://docs.i2p-projekt.de/javadoc/
junitdocs.url=http://junit.org/apidocs/ junitdocs.url=http://junit.org/apidocs/
# This will go in the jar manifests
build.built-by=unknown

View File

@@ -166,6 +166,7 @@
<zipfileset src="apps/jrobin/jrobin-1.5.9.1.jar" <zipfileset src="apps/jrobin/jrobin-1.5.9.1.jar"
excludes="org/jrobin/cmd/ org/jrobin/convertor/ org/jrobin/inspector/" /> excludes="org/jrobin/cmd/ org/jrobin/convertor/ org/jrobin/inspector/" />
<manifest> <manifest>
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
</manifest> </manifest>
@@ -206,6 +207,7 @@
<copy file="apps/jetty/jettylib/javax.servlet.jar" todir="build/" /> <copy file="apps/jetty/jettylib/javax.servlet.jar" todir="build/" />
</target> </target>
<!-- this makes an empty build/launchi2p.jar and the build/i2p.exe for the no-wrapper windows startup, if possible -->
<target name="buildexe"> <target name="buildexe">
<condition property="noExe"> <condition property="noExe">
<os arch="x86_64" /> <os arch="x86_64" />
@@ -234,11 +236,13 @@
classname="net.sf.launch4j.ant.Launch4jTask" classname="net.sf.launch4j.ant.Launch4jTask"
classpath="${basedir}/installer/lib/launch4j/launch4j.jar:${basedir}/installer/lib/launch4j/lib/xstream.jar" /> classpath="${basedir}/installer/lib/launch4j/launch4j.jar:${basedir}/installer/lib/launch4j/lib/xstream.jar" />
<!-- this makes an empty build/launchi2p.jar and the build/i2p.exe for the no-wrapper windows startup -->
<target name="doBuildEXE" depends="buildProperties" unless="noExe"> <target name="doBuildEXE" depends="buildProperties" unless="noExe">
<jar destfile="./build/launchi2p.jar"> <jar destfile="./build/launchi2p.jar">
<manifest> <manifest>
<attribute name="Main-Class" value="net.i2p.router.RouterLaunch" /> <attribute name="Main-Class" value="net.i2p.router.RouterLaunch" />
<attribute name="Class-Path" value="lib/i2p.jar lib/router.jar lib/jbigi.jar lib/BOB.jar lib/sam.jar lib/mstreaming.jar lib/streaming.jar lib/routerconsole.jar lib/i2ptunnel.jar lib/org.mortbay.jetty.jar lib/javax.servlet.jar lib/jasper-compiler.jar lib/jasper-runtime.jar lib/commons-logging.jar lib/commons-el.jar lib/wrapper.jar lib/systray.jar lib/systray4j.jar lib/desktopgui.jar" /> <attribute name="Class-Path" value="lib/i2p.jar lib/router.jar lib/jbigi.jar lib/BOB.jar lib/sam.jar lib/mstreaming.jar lib/streaming.jar lib/routerconsole.jar lib/i2ptunnel.jar lib/org.mortbay.jetty.jar lib/javax.servlet.jar lib/jasper-compiler.jar lib/jasper-runtime.jar lib/commons-logging.jar lib/commons-el.jar lib/wrapper.jar lib/systray.jar lib/systray4j.jar lib/desktopgui.jar" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
</manifest> </manifest>
@@ -289,6 +293,7 @@
<jar destfile="build/jbigi.jar" whenmanifestonly="fail" > <jar destfile="build/jbigi.jar" whenmanifestonly="fail" >
<fileset dir="installer/lib/jbigi" includes="*.so *.dll *.jnilib" /> <fileset dir="installer/lib/jbigi" includes="*.so *.dll *.jnilib" />
<manifest> <manifest>
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.jbigi.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.jbigi.tr}" />
@@ -302,6 +307,7 @@
<jar destfile="build/jbigi.jar" whenmanifestonly="fail" > <jar destfile="build/jbigi.jar" whenmanifestonly="fail" >
<fileset dir="installer/lib/jbigi" includes="*.so *.jnilib" /> <fileset dir="installer/lib/jbigi" includes="*.so *.jnilib" />
<manifest> <manifest>
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.jbigi.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.jbigi.tr}" />
@@ -316,6 +322,7 @@
<jar destfile="build/jbigi.jar" whenmanifestonly="fail" > <jar destfile="build/jbigi.jar" whenmanifestonly="fail" >
<fileset dir="installer/lib/jbigi" includes="*freebsd*.so" /> <fileset dir="installer/lib/jbigi" includes="*freebsd*.so" />
<manifest> <manifest>
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.jbigi.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.jbigi.tr}" />
@@ -329,6 +336,7 @@
<jar destfile="build/jbigi.jar" whenmanifestonly="fail" > <jar destfile="build/jbigi.jar" whenmanifestonly="fail" >
<fileset dir="installer/lib/jbigi" includes="*linux*.so" /> <fileset dir="installer/lib/jbigi" includes="*linux*.so" />
<manifest> <manifest>
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.jbigi.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.jbigi.tr}" />
@@ -342,6 +350,7 @@
<jar destfile="build/jbigi.jar" whenmanifestonly="fail" > <jar destfile="build/jbigi.jar" whenmanifestonly="fail" >
<fileset dir="installer/lib/jbigi" includes="*linux-arm*.so,*linux-ppc*.so" /> <fileset dir="installer/lib/jbigi" includes="*linux-arm*.so,*linux-ppc*.so" />
<manifest> <manifest>
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.jbigi.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.jbigi.tr}" />
@@ -355,6 +364,7 @@
<jar destfile="build/jbigi.jar" whenmanifestonly="fail" > <jar destfile="build/jbigi.jar" whenmanifestonly="fail" >
<fileset dir="installer/lib/jbigi" includes="*.jnilib" /> <fileset dir="installer/lib/jbigi" includes="*.jnilib" />
<manifest> <manifest>
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.jbigi.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.jbigi.tr}" />
@@ -368,6 +378,7 @@
<jar destfile="build/jbigi.jar" whenmanifestonly="fail" > <jar destfile="build/jbigi.jar" whenmanifestonly="fail" >
<fileset dir="installer/lib/jbigi" includes="*windows*.dll" /> <fileset dir="installer/lib/jbigi" includes="*windows*.dll" />
<manifest> <manifest>
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.jbigi.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.jbigi.tr}" />
@@ -544,6 +555,7 @@
</target> </target>
<target name="verifyReleaseBuildNumbers" depends="getReleaseNumber, getBuildNumber" > <target name="verifyReleaseBuildNumbers" depends="getReleaseNumber, getBuildNumber" >
<echo message="SDK: ${java.vendor} ${java.version} (${java.runtime.name} ${java.runtime.version})" />
<fail message="Bad release number: ${release.number}" > <fail message="Bad release number: ${release.number}" >
<condition> <condition>
<or> <or>
@@ -572,7 +584,6 @@
<delete dir="./build" /> <delete dir="./build" />
<delete file="installer/lib/izpack/patches.jar" failonerror="false" quiet="true" /> <delete file="installer/lib/izpack/patches.jar" failonerror="false" quiet="true" />
<delete file="i2pinstall.exe" failonerror="false" quiet="true" /> <delete file="i2pinstall.exe" failonerror="false" quiet="true" />
<delete file="i2p.exe" failonerror="false" quiet="true" />
<delete file="syndie-standalone.zip" failonerror="false" quiet="true" /> <delete file="syndie-standalone.zip" failonerror="false" quiet="true" />
<delete> <delete>
<fileset dir="." includes="i2pinstall*jar i2pinstall*bz2" /> <fileset dir="." includes="i2pinstall*jar i2pinstall*bz2" />
@@ -754,7 +765,7 @@
</target> </target>
<target name="preppkg-windows" depends="preppkg-base, buildexe"> <target name="preppkg-windows" depends="preppkg-base, buildexe">
<copy file="i2p.exe" todir="pkg-temp/" failonerror="false" /> <copy file="build/i2p.exe" todir="pkg-temp/" failonerror="false" />
<copy file="apps/systray/java/lib/systray4j.dll" todir="pkg-temp/lib" /> <copy file="apps/systray/java/lib/systray4j.dll" todir="pkg-temp/lib" />
<copy file="apps/systray/java/resources/iggy.ico" todir="pkg-temp/icons" /> <copy file="apps/systray/java/resources/iggy.ico" todir="pkg-temp/icons" />
<copy file="apps/systray/java/resources/iggy.xpm" todir="pkg-temp/icons" /> <copy file="apps/systray/java/resources/iggy.xpm" todir="pkg-temp/icons" />
@@ -1016,7 +1027,9 @@
<copy file="build/i2psnark.jar" todir="pkg-temp/lib" /> <copy file="build/i2psnark.jar" todir="pkg-temp/lib" />
<!-- include systray changes in 0.7.5 --> <!-- include systray changes in 0.7.5 -->
<copy file="build/systray.jar" todir="pkg-temp/lib/" /> <copy file="build/systray.jar" todir="pkg-temp/lib/" />
<!-- removed from updater in 0.9
<copy file="build/desktopgui.jar" todir="pkg-temp/lib/" /> <copy file="build/desktopgui.jar" todir="pkg-temp/lib/" />
-->
<copy file="build/susimail.war" todir="pkg-temp/webapps/" /> <copy file="build/susimail.war" todir="pkg-temp/webapps/" />
<copy file="build/susidns.war" todir="pkg-temp/webapps/" /> <copy file="build/susidns.war" todir="pkg-temp/webapps/" />
<!-- as of 0.7.12; someday, we can remove these from the updater --> <!-- as of 0.7.12; someday, we can remove these from the updater -->
@@ -1116,6 +1129,7 @@
<jar destfile="./pkg-temp/installer/copy.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Copy.class net/i2p/util/FileUtil.class"> <jar destfile="./pkg-temp/installer/copy.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Copy.class net/i2p/util/FileUtil.class">
<manifest> <manifest>
<attribute name="Main-Class" value="net.i2p.util.Copy" /> <attribute name="Main-Class" value="net.i2p.util.Copy" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.util.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.util.tr}" />
@@ -1124,6 +1138,7 @@
<jar destfile="./pkg-temp/installer/delete.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Delete.class net/i2p/util/FileUtil.class"> <jar destfile="./pkg-temp/installer/delete.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Delete.class net/i2p/util/FileUtil.class">
<manifest> <manifest>
<attribute name="Main-Class" value="net.i2p.util.Delete" /> <attribute name="Main-Class" value="net.i2p.util.Delete" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.util.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.util.tr}" />
@@ -1132,6 +1147,7 @@
<jar destfile="./pkg-temp/installer/exec.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Exec.class"> <jar destfile="./pkg-temp/installer/exec.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Exec.class">
<manifest> <manifest>
<attribute name="Main-Class" value="net.i2p.util.Exec" /> <attribute name="Main-Class" value="net.i2p.util.Exec" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.util.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.util.tr}" />
@@ -1169,43 +1185,31 @@
<ant target="doInstallerEXE" /> <ant target="doInstallerEXE" />
</target> </target>
<!-- this makes i2pinstall.exe from install.jar -->
<target name="doInstallerEXE" unless="noExe"> <target name="doInstallerEXE" unless="noExe">
<!-- now the installer exe --> <!-- now the installer exe -->
<launch4j configFile="./installer/i2pinstaller.xml" /> <launch4j configFile="./installer/i2pinstaller.xml" />
<launch4j configFile="./installer/i2pstandalone.xml" />
<!-- thazzit --> <!-- thazzit -->
</target> </target>
<!-- Custom installers --> <!-- Custom installers -->
<target name="installer-nowindows" depends="clean, preppkg-nowindows, getReleaseNumber, getBuildNumber, buildProperties, util-list-changes, izpack-patches" > <target name="installer-nowindows" depends="clean, preppkg-nowindows, izpack-patches" >
<mkdir dir="pkg-temp/installer" /> <izpack input="${basedir}/installer/install.xml" output="${basedir}/i2pinstall_${full.version}.jar" installerType="standard" basedir="${basedir}" />
<!-- set if unset -->
<property name="workspace.changes.util.tr" value="" />
<izpack input="${basedir}/installer/install.xml" output="${basedir}/i2pinstall_${release.number}-${i2p.build.number}${build.extra}.jar" installerType="standard" basedir="${basedir}" />
</target> </target>
<target name="installer-freebsd" depends="clean, preppkg-freebsd-only, getReleaseNumber, getBuildNumber, buildProperties, util-list-changes, izpack-patches" > <target name="installer-freebsd" depends="clean, preppkg-freebsd-only, izpack-patches" >
<mkdir dir="pkg-temp/installer" /> <izpack input="${basedir}/installer/install.xml" output="${basedir}/i2pinstall_${full.version}_freebsd-only.jar" installerType="standard" basedir="${basedir}" />
<!-- set if unset -->
<property name="workspace.changes.util.tr" value="" />
<izpack input="${basedir}/installer/install.xml" output="${basedir}/i2pinstall_${release.number}-${i2p.build.number}${build.extra}_freebsd-only.jar" installerType="standard" basedir="${basedir}" />
</target> </target>
<target name="installer-linux" depends="clean, preppkg-linux-only, getReleaseNumber, getBuildNumber, buildProperties, util-list-changes, izpack-patches" > <target name="installer-linux" depends="clean, preppkg-linux-only, izpack-patches" >
<mkdir dir="pkg-temp/installer" /> <izpack input="${basedir}/installer/install.xml" output="${basedir}/i2pinstall_${full.version}_linux-only.jar" installerType="standard" basedir="${basedir}" />
<!-- set if unset -->
<property name="workspace.changes.util.tr" value="" />
<izpack input="${basedir}/installer/install.xml" output="${basedir}/i2pinstall_${release.number}-${i2p.build.number}${build.extra}_linux-only.jar" installerType="standard" basedir="${basedir}" />
</target> </target>
<target name="installer-osx" depends="clean, checkForIzpack2App, preppkg-osx-only, getReleaseNumber, getBuildNumber, buildProperties, util-list-changes, izpack-patches"> <target name="installer-osx" depends="clean, checkForIzpack2App, preppkg-osx-only, izpack-patches">
<mkdir dir="pkg-temp/installer" />
<mkdir dir="pkg-temp/osx" /> <mkdir dir="pkg-temp/osx" />
<!-- set if unset --> <izpack input="${basedir}/installer/install.xml" output="${basedir}/i2pinstall_${full.version}_osx-only.jar" installerType="standard" basedir="${basedir}" />
<property name="workspace.changes.util.tr" value="" />
<izpack input="${basedir}/installer/install.xml" output="${basedir}/i2pinstall_${release.number}-${i2p.build.number}${build.extra}_osx-only.jar" installerType="standard" basedir="${basedir}" />
<ant target="installer2app" /> <ant target="installer2app" />
<delete dir="pkg-temp/osx" /> <delete dir="pkg-temp/osx" />
</target> </target>
@@ -1218,34 +1222,32 @@
<mkdir dir="pkg-temp/osx" /> <mkdir dir="pkg-temp/osx" />
<exec executable="python" failonerror="true"> <exec executable="python" failonerror="true">
<arg value="${user.home}/IzPack/utils/wrappers/izpack2app/izpack2app.py" /> <arg value="${user.home}/IzPack/utils/wrappers/izpack2app/izpack2app.py" />
<arg value="${basedir}/i2pinstall_${release.number}-${i2p.build.number}${build.extra}_osx-only.jar" /> <arg value="${basedir}/i2pinstall_${full.version}_osx-only.jar" />
<arg value="${basedir}/pkg-temp/osx/i2p-${release.number}-${i2p.build.number}${build.extra}_osx-install.app" /> <arg value="${basedir}/pkg-temp/osx/i2p-${full.version}_osx-install.app" />
</exec> </exec>
<exec executable="chmod" failonerror="true" osfamily="unix"> <exec executable="chmod" failonerror="true" osfamily="unix">
<arg value="755" /> <arg value="755" />
<arg value="${basedir}/pkg-temp/osx/i2p-${release.number}-${i2p.build.number}${build.extra}_osx-install.app/Contents/MacOS/JavaApplicationStub" /> <arg value="${basedir}/pkg-temp/osx/i2p-${full.version}_osx-install.app/Contents/MacOS/JavaApplicationStub" />
</exec> </exec>
<exec executable="tar" osfamily="unix" failonerror="true"> <exec executable="tar" osfamily="unix" failonerror="true">
<arg value="--owner=root" /> <arg value="--owner=root" />
<arg value="--group=root" /> <arg value="--group=root" />
<arg value="-cjvf" /> <arg value="-cjvf" />
<arg value="${basedir}/i2pinstall_${release.number}-${i2p.build.number}${build.extra}_osx.tar.bz2" /> <arg value="${basedir}/i2pinstall_${full.version}_osx.tar.bz2" />
<arg value="-C" /> <arg value="-C" />
<arg value="${basedir}/pkg-temp/osx" /> <arg value="${basedir}/pkg-temp/osx" />
<arg value="i2p-${release.number}-${i2p.build.number}${build.extra}_osx-install.app" /> <arg value="i2p-${full.version}_osx-install.app" />
</exec> </exec>
</target> </target>
<!-- Yes, even though this makes an installer for Windows, we still need to depend on preppkg-unix (because the installer attempts to delete these files <target name="installer-windows" depends="clean, preppkg-windows-only, util-list-changes, izpack-patches" >
and will fail if they don't exist. Thankfully these files are so very small there's not much harm in including them.
-->
<target name="installer-windows" depends="clean, doBuildEXE, preppkg-windows-only, preppkg-unix, getReleaseNumber, getBuildNumber, buildProperties, util-list-changes, izpack-patches" >
<mkdir dir="pkg-temp/installer" /> <mkdir dir="pkg-temp/installer" />
<!-- set if unset --> <!-- set if unset -->
<property name="workspace.changes.util.tr" value="" /> <property name="workspace.changes.util.tr" value="" />
<jar destfile="./pkg-temp/installer/copy.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Copy.class net/i2p/util/FileUtil.class"> <jar destfile="./pkg-temp/installer/copy.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Copy.class net/i2p/util/FileUtil.class">
<manifest> <manifest>
<attribute name="Main-Class" value="net.i2p.util.Copy" /> <attribute name="Main-Class" value="net.i2p.util.Copy" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.util.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.util.tr}" />
@@ -1254,6 +1256,7 @@
<jar destfile="./pkg-temp/installer/delete.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Delete.class net/i2p/util/FileUtil.class"> <jar destfile="./pkg-temp/installer/delete.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Delete.class net/i2p/util/FileUtil.class">
<manifest> <manifest>
<attribute name="Main-Class" value="net.i2p.util.Delete" /> <attribute name="Main-Class" value="net.i2p.util.Delete" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.util.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.util.tr}" />
@@ -1262,13 +1265,16 @@
<jar destfile="./pkg-temp/installer/exec.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Exec.class"> <jar destfile="./pkg-temp/installer/exec.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Exec.class">
<manifest> <manifest>
<attribute name="Main-Class" value="net.i2p.util.Exec" /> <attribute name="Main-Class" value="net.i2p.util.Exec" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.util.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.util.tr}" />
</manifest> </manifest>
</jar> </jar>
<izpack input="${basedir}/installer/install.xml" output="${basedir}/i2pinstall_${release.number}-${i2p.build.number}${build.extra}_windows-only.jar" installerType="standard" basedir="${basedir}" /> <izpack input="${basedir}/installer/install.xml" output="${basedir}/install.jar" installerType="standard" basedir="${basedir}" />
<delete dir="pkg-temp/win" /> <ant target="installerexe" />
<delete file="${basedir}/install.jar" />
<move file="${basedir}/i2pinstall.exe" tofile="${basedir}/i2pinstall_${full.version}_windows-only.exe" />
</target> </target>
<target name="installer-all" depends="installer-freebsd, installer-linux, installer-osx, installer-windows, installer" > <target name="installer-all" depends="installer-freebsd, installer-linux, installer-osx, installer-windows, installer" >
@@ -1383,8 +1389,9 @@
<echo message="New version number is ${release.number}" /> <echo message="New version number is ${release.number}" />
<copy file="i2pupdate.zip" tofile="i2pupdate_${release.number}.zip" /> <copy file="i2pupdate.zip" tofile="i2pupdate_${release.number}.zip" />
<copy file="i2pinstall.exe" tofile="i2pinstall_${release.number}.exe" /> <copy file="i2pinstall.exe" tofile="i2pinstall_${release.number}.exe" />
<delete file="i2pupdate.sud" failonerror="false" /> <delete file="i2pupdate.sud" />
<delete file="i2pupdate.su2" failonerror="false" /> <delete file="i2pupdate.su2" />
<!-- make this a lot easier by putting release.privkey=/path/to/privkey in override.properties -->
<input message="Enter private signing key file:" addproperty="release.privkey" /> <input message="Enter private signing key file:" addproperty="release.privkey" />
<fail message="You must enter a path." > <fail message="You must enter a path." >
<condition> <condition>
@@ -1494,6 +1501,17 @@
<arg value="-b" /> <arg value="-b" />
<arg value="i2pupdate_${release.number}.zip" /> <arg value="i2pupdate_${release.number}.zip" />
</exec> </exec>
<exec executable="chmod" failonerror="true">
<arg value="444" />
<arg value="i2pinstall_${release.number}.exe" />
<arg value="i2psource_${release.number}.tar.bz2" />
<arg value="i2pupdate_${release.number}.zip" />
<arg value="i2pupdate.su2" />
<arg value="i2pupdate.sud" />
<arg value="i2pinstall_${release.number}.exe.sig" />
<arg value="i2psource_${release.number}.tar.bz2.sig" />
<arg value="i2pupdate_${release.number}.zip.sig" />
</exec>
<echo message="File sizes:" /> <echo message="File sizes:" />
<exec executable="ls" failonerror="true"> <exec executable="ls" failonerror="true">
<arg value="-l" /> <arg value="-l" />

View File

@@ -59,6 +59,7 @@
<jar destfile="./build/i2p.jar" basedir="./build/obj" includes="**/*.class" > <jar destfile="./build/i2p.jar" basedir="./build/obj" includes="**/*.class" >
<manifest> <manifest>
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" />

View File

@@ -34,7 +34,7 @@ import net.i2p.util.Log;
* @author jrandom * @author jrandom
*/ */
class RequestLeaseSetMessageHandler extends HandlerImpl { class RequestLeaseSetMessageHandler extends HandlerImpl {
private final Map _existingLeaseSets; private final Map<Destination, LeaseInfo> _existingLeaseSets;
public RequestLeaseSetMessageHandler(I2PAppContext context) { public RequestLeaseSetMessageHandler(I2PAppContext context) {
super(context, RequestLeaseSetMessage.MESSAGE_TYPE); super(context, RequestLeaseSetMessage.MESSAGE_TYPE);
@@ -59,7 +59,7 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
leaseSet.setDestination(session.getMyDestination()); leaseSet.setDestination(session.getMyDestination());
// reuse the old keys for the client // reuse the old keys for the client
LeaseInfo li = (LeaseInfo) _existingLeaseSets.get(session.getMyDestination()); LeaseInfo li = _existingLeaseSets.get(session.getMyDestination());
if (li == null) { if (li == null) {
li = new LeaseInfo(session.getMyDestination()); li = new LeaseInfo(session.getMyDestination());
_existingLeaseSets.put(session.getMyDestination(), li); _existingLeaseSets.put(session.getMyDestination(), li);
@@ -98,11 +98,11 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
} }
private static class LeaseInfo { private static class LeaseInfo {
private PublicKey _pubKey; private final PublicKey _pubKey;
private PrivateKey _privKey; private final PrivateKey _privKey;
private SigningPublicKey _signingPubKey; private final SigningPublicKey _signingPubKey;
private SigningPrivateKey _signingPrivKey; private final SigningPrivateKey _signingPrivKey;
private Destination _dest; private final Destination _dest;
public LeaseInfo(Destination dest) { public LeaseInfo(Destination dest) {
_dest = dest; _dest = dest;

View File

@@ -92,6 +92,7 @@ public class BlockfileNamingService extends DummyNamingService {
private final RAIFile _raf; private final RAIFile _raf;
private final List<String> _lists; private final List<String> _lists;
private final List<InvalidEntry> _invalid; private final List<InvalidEntry> _invalid;
private final Map<String, String> _negativeCache;
private volatile boolean _isClosed; private volatile boolean _isClosed;
private final boolean _readOnly; private final boolean _readOnly;
private boolean _needsUpgrade; private boolean _needsUpgrade;
@@ -118,6 +119,9 @@ public class BlockfileNamingService extends DummyNamingService {
private static final String PROP_ADDED = "a"; private static final String PROP_ADDED = "a";
private static final String PROP_SOURCE = "s"; private static final String PROP_SOURCE = "s";
private static final String DUMMY = "";
private static final int NEGATIVE_CACHE_SIZE = 32;
/** /**
* Opens the database at hostsdb.blockfile or creates a new * Opens the database at hostsdb.blockfile or creates a new
* one and imports entries from hosts.txt, userhosts.txt, and privatehosts.txt. * one and imports entries from hosts.txt, userhosts.txt, and privatehosts.txt.
@@ -132,6 +136,7 @@ public class BlockfileNamingService extends DummyNamingService {
super(context); super(context);
_lists = new ArrayList(); _lists = new ArrayList();
_invalid = new ArrayList(); _invalid = new ArrayList();
_negativeCache = new LHM(NEGATIVE_CACHE_SIZE);
BlockFile bf = null; BlockFile bf = null;
RAIFile raf = null; RAIFile raf = null;
boolean readOnly = false; boolean readOnly = false;
@@ -628,6 +633,10 @@ public class BlockfileNamingService extends DummyNamingService {
} }
String key = hostname.toLowerCase(Locale.US); String key = hostname.toLowerCase(Locale.US);
synchronized(_negativeCache) {
if (_negativeCache.get(key) != null)
return null;
}
synchronized(_bf) { synchronized(_bf) {
if (_isClosed) if (_isClosed)
return null; return null;
@@ -650,8 +659,13 @@ public class BlockfileNamingService extends DummyNamingService {
} }
deleteInvalid(); deleteInvalid();
} }
if (d != null) if (d != null) {
putCache(hostname, d); putCache(hostname, d);
} else {
synchronized(_negativeCache) {
_negativeCache.put(key, DUMMY);
}
}
return d; return d;
} }
@@ -683,6 +697,9 @@ public class BlockfileNamingService extends DummyNamingService {
return false; return false;
} }
String key = hostname.toLowerCase(Locale.US); String key = hostname.toLowerCase(Locale.US);
synchronized(_negativeCache) {
_negativeCache.remove(key);
}
String listname = FALLBACK_LIST; String listname = FALLBACK_LIST;
Properties props = new Properties(); Properties props = new Properties();
props.setProperty(PROP_ADDED, Long.toString(_context.clock().now())); props.setProperty(PROP_ADDED, Long.toString(_context.clock().now()));
@@ -1031,6 +1048,9 @@ public class BlockfileNamingService extends DummyNamingService {
} }
_isClosed = true; _isClosed = true;
} }
synchronized(_negativeCache) {
_negativeCache.clear();
}
clearCache(); clearCache();
} }

View File

@@ -116,7 +116,7 @@ class DummyNamingService extends NamingService {
} }
} }
private static class LHM<K, V> extends LinkedHashMap<K, V> { protected static class LHM<K, V> extends LinkedHashMap<K, V> {
private final int _max; private final int _max;
public LHM(int max) { public LHM(int max) {

View File

@@ -625,7 +625,7 @@ public class TransientSessionKeyManager extends SessionKeyManager {
OutboundSession sess = iter.next(); OutboundSession sess = iter.next();
Set<TagSet> sets = new TreeSet(new TagSetComparator()); Set<TagSet> sets = new TreeSet(new TagSetComparator());
sets.addAll(sess.getTagSets()); sets.addAll(sess.getTagSets());
buf.append("<tr><td><b>Target key:</b> ").append(sess.getTarget().toBase64().substring(0, 64)).append("<br>" + buf.append("<tr><td><b>Target public key:</b> ").append(sess.getTarget().toBase64().substring(0, 20)).append("...<br>" +
"<b>Established:</b> ").append(DataHelper.formatDuration(now - sess.getEstablishedDate())).append(" ago<br>" + "<b>Established:</b> ").append(DataHelper.formatDuration(now - sess.getEstablishedDate())).append(" ago<br>" +
"<b>Last Used:</b> ").append(DataHelper.formatDuration(now - sess.getLastUsedDate())).append(" ago<br>" + "<b>Last Used:</b> ").append(DataHelper.formatDuration(now - sess.getLastUsedDate())).append(" ago<br>" +
"<b>Session key:</b> ").append(sess.getCurrentKey().toBase64()).append("</td>" + "<b>Session key:</b> ").append(sess.getCurrentKey().toBase64()).append("</td>" +

View File

@@ -29,7 +29,7 @@ import net.i2p.util.Log;
*/ */
public class Base32 { public class Base32 {
private final static Log _log = new Log(Base32.class); //private final static Log _log = new Log(Base32.class);
/** The 32 valid Base32 values. */ /** The 32 valid Base32 values. */
private final static char[] ALPHABET = {'a', 'b', 'c', 'd', private final static char[] ALPHABET = {'a', 'b', 'c', 'd',
@@ -248,12 +248,12 @@ public class Base32 {
outBuff[outBuffPosn] = next; outBuff[outBuffPosn] = next;
usedbits -= 3; usedbits -= 3;
} else if (next != 0) { } else if (next != 0) {
_log.warn("Extra data at the end: " + next + "(decimal)"); //_log.warn("Extra data at the end: " + next + "(decimal)");
return null; return null;
} }
} }
} else { } else {
_log.warn("Bad Base32 input character at " + i + ": " + source[i] + "(decimal)"); //_log.warn("Bad Base32 input character at " + i + ": " + source[i] + "(decimal)");
return null; return null;
} }
} }

View File

@@ -41,7 +41,7 @@ import net.i2p.util.Log;
*/ */
public class Base64 { public class Base64 {
private final static Log _log = new Log(Base64.class); //private final static Log _log = new Log(Base64.class);
/** /**
* @param source if null will return "" * @param source if null will return ""
@@ -750,7 +750,7 @@ public class Base64 {
} // end if: white space, equals sign or better } // end if: white space, equals sign or better
else { else {
_log.warn("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)"); //_log.warn("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)");
return null; return null;
} // end else: } // end else:
} // each input character } // each input character

View File

@@ -1299,10 +1299,20 @@ public class DataHelper {
// rv.add(struct); // rv.add(struct);
//} //}
ArrayList<DataStructure> rv = new ArrayList(dataStructures); ArrayList<DataStructure> rv = new ArrayList(dataStructures);
Collections.sort(rv, new DataStructureComparator()); sortStructureList(rv);
return rv; return rv;
} }
/**
* See above.
* DEPRECATED - Only used by RouterInfo.
*
* @since 0.9
*/
static void sortStructureList(List<? extends DataStructure> dataStructures) {
Collections.sort(dataStructures, new DataStructureComparator());
}
/** /**
* See sortStructures() comments. * See sortStructures() comments.
* @since 0.8.3 * @since 0.8.3

View File

@@ -14,6 +14,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import net.i2p.I2PAppContext;
import net.i2p.crypto.SHA256Generator; import net.i2p.crypto.SHA256Generator;
import net.i2p.util.Log; import net.i2p.util.Log;
@@ -23,7 +24,6 @@ import net.i2p.util.Log;
* @author jrandom * @author jrandom
*/ */
public abstract class DataStructureImpl implements DataStructure { public abstract class DataStructureImpl implements DataStructure {
private final static Log _log = new Log(DataStructureImpl.class);
public String toBase64() { public String toBase64() {
byte data[] = toByteArray(); byte data[] = toByteArray();
@@ -48,10 +48,12 @@ public abstract class DataStructureImpl implements DataStructure {
writeBytes(baos); writeBytes(baos);
return baos.toByteArray(); return baos.toByteArray();
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error writing out the byte array", ioe); Log log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
log.error("Error writing out the byte array", ioe);
return null; return null;
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
_log.error("Error writing out the byte array", dfe); Log log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
log.error("Error writing out the byte array", dfe);
return null; return null;
} }
} }
@@ -73,4 +75,4 @@ public abstract class DataStructureImpl implements DataStructure {
protected int read(InputStream in, byte target[]) throws IOException { protected int read(InputStream in, byte target[]) throws IOException {
return DataHelper.read(in, target); return DataHelper.read(in, target);
} }
} }

View File

@@ -26,8 +26,8 @@ public class Lease extends DataStructureImpl {
private Hash _gateway; private Hash _gateway;
private TunnelId _tunnelId; private TunnelId _tunnelId;
private Date _end; private Date _end;
private int _numSuccess; //private int _numSuccess;
private int _numFailure; //private int _numFailure;
public Lease() { public Lease() {
} }
@@ -74,14 +74,18 @@ public class Lease extends DataStructureImpl {
* *
* @deprecated unused * @deprecated unused
*/ */
/****
public int getNumSuccess() { public int getNumSuccess() {
return _numSuccess; return _numSuccess;
} }
****/
/** @deprecated unused */ /** @deprecated unused */
/****
public void setNumSuccess(int num) { public void setNumSuccess(int num) {
_numSuccess = num; _numSuccess = num;
} }
****/
/** /**
* Transient attribute of the lease, used to note how many times messages sent * Transient attribute of the lease, used to note how many times messages sent
@@ -89,14 +93,18 @@ public class Lease extends DataStructureImpl {
* *
* @deprecated unused * @deprecated unused
*/ */
/****
public int getNumFailure() { public int getNumFailure() {
return _numFailure; return _numFailure;
} }
****/
/** @deprecated unused */ /** @deprecated unused */
/****
public void setNumFailure(int num) { public void setNumFailure(int num) {
_numFailure = num; _numFailure = num;
} }
****/
/** has this lease already expired? */ /** has this lease already expired? */
public boolean isExpired() { public boolean isExpired() {

View File

@@ -56,7 +56,6 @@ import net.i2p.util.RandomSource;
* @author jrandom * @author jrandom
*/ */
public class LeaseSet extends DatabaseEntry { public class LeaseSet extends DatabaseEntry {
private final static Log _log = new Log(LeaseSet.class);
private Destination _destination; private Destination _destination;
private PublicKey _encryptionKey; private PublicKey _encryptionKey;
private SigningPublicKey _signingKey; private SigningPublicKey _signingKey;
@@ -71,11 +70,26 @@ public class LeaseSet extends DatabaseEntry {
private boolean _decrypted; private boolean _decrypted;
private boolean _checked; private boolean _checked;
/** This seems like plenty */ /**
public final static int MAX_LEASES = 6; * Unlimited before 0.6.3;
* 6 as of 0.6.3;
* Increased in version 0.9.
*
* Leasesets larger than 6 should be used with caution,
* as each lease adds 44 bytes, and routers older than version 0.9
* will not be able to connect as they will throw an exception in
* readBytes(). Also, the churn will be quite rapid, leading to
* frequent netdb stores and transmission on existing connections.
*
* However we increase it now in case some hugely popular eepsite arrives.
* Strategies elsewhere in the router to efficiently handle
* large leasesets are TBD.
*/
public static final int MAX_LEASES = 16;
private static final int OLD_MAX_LEASES = 6;
public LeaseSet() { public LeaseSet() {
_leases = new ArrayList(MAX_LEASES); _leases = new ArrayList(OLD_MAX_LEASES);
_firstExpiration = Long.MAX_VALUE; _firstExpiration = Long.MAX_VALUE;
} }
@@ -354,14 +368,16 @@ public class LeaseSet extends DatabaseEntry {
* Must be called after all the leases are in place, but before sign(). * Must be called after all the leases are in place, but before sign().
*/ */
public void encrypt(SessionKey key) { public void encrypt(SessionKey key) {
if (_log.shouldLog(Log.WARN)) //if (_log.shouldLog(Log.WARN))
_log.warn("encrypting lease: " + _destination.calculateHash()); // _log.warn("encrypting lease: " + _destination.calculateHash());
try { try {
encryp(key); encryp(key);
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
_log.error("Error encrypting lease: " + _destination.calculateHash()); Log log = I2PAppContext.getGlobalContext().logManager().getLog(LeaseSet.class);
log.error("Error encrypting lease: " + _destination.calculateHash());
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error encrypting lease: " + _destination.calculateHash()); Log log = I2PAppContext.getGlobalContext().logManager().getLog(LeaseSet.class);
log.error("Error encrypting lease: " + _destination.calculateHash());
} }
} }
@@ -420,8 +436,8 @@ public class LeaseSet extends DatabaseEntry {
* encrypted leaseset can be sent on to others (via writeBytes()) * encrypted leaseset can be sent on to others (via writeBytes())
*/ */
private void decrypt(SessionKey key) throws DataFormatException, IOException { private void decrypt(SessionKey key) throws DataFormatException, IOException {
if (_log.shouldLog(Log.WARN)) //if (_log.shouldLog(Log.WARN))
_log.warn("decrypting lease: " + _destination.calculateHash()); // _log.warn("decrypting lease: " + _destination.calculateHash());
int size = _leases.size(); int size = _leases.size();
if (size < 2) if (size < 2)
throw new DataFormatException("Bad number of leases for decryption"); throw new DataFormatException("Bad number of leases for decryption");
@@ -468,9 +484,11 @@ public class LeaseSet extends DatabaseEntry {
decrypt(key); decrypt(key);
_decrypted = true; _decrypted = true;
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
_log.error("Error decrypting lease: " + _destination.calculateHash() + dfe); Log log = I2PAppContext.getGlobalContext().logManager().getLog(LeaseSet.class);
log.error("Error decrypting lease: " + _destination.calculateHash() + dfe);
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error decrypting lease: " + _destination.calculateHash() + ioe); Log log = I2PAppContext.getGlobalContext().logManager().getLog(LeaseSet.class);
log.error("Error decrypting lease: " + _destination.calculateHash() + ioe);
} }
} }
_checked = true; _checked = true;

View File

@@ -26,7 +26,7 @@ import net.i2p.util.Log;
* @author jrandom * @author jrandom
*/ */
public class Payload extends DataStructureImpl { public class Payload extends DataStructureImpl {
private final static Log _log = new Log(Payload.class); //private final static Log _log = new Log(Payload.class);
private byte[] _encryptedData; private byte[] _encryptedData;
private byte[] _unencryptedData; private byte[] _unencryptedData;
@@ -82,16 +82,16 @@ public class Payload extends DataStructureImpl {
_encryptedData = new byte[size]; _encryptedData = new byte[size];
int read = read(in, _encryptedData); int read = read(in, _encryptedData);
if (read != size) throw new DataFormatException("Incorrect number of bytes read in the payload structure"); if (read != size) throw new DataFormatException("Incorrect number of bytes read in the payload structure");
if (_log.shouldLog(Log.DEBUG)) //if (_log.shouldLog(Log.DEBUG))
_log.debug("read payload: " + read + " bytes"); // _log.debug("read payload: " + read + " bytes");
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_encryptedData == null) throw new DataFormatException("Not yet encrypted. Please set the encrypted data"); if (_encryptedData == null) throw new DataFormatException("Not yet encrypted. Please set the encrypted data");
DataHelper.writeLong(out, 4, _encryptedData.length); DataHelper.writeLong(out, 4, _encryptedData.length);
out.write(_encryptedData); out.write(_encryptedData);
if (_log.shouldLog(Log.DEBUG)) //if (_log.shouldLog(Log.DEBUG))
_log.debug("wrote payload: " + _encryptedData.length); // _log.debug("wrote payload: " + _encryptedData.length);
} }
/** /**

View File

@@ -13,6 +13,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
@@ -24,6 +25,7 @@ import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.Vector; import java.util.Vector;
import net.i2p.I2PAppContext;
import net.i2p.crypto.SHA256Generator; import net.i2p.crypto.SHA256Generator;
import net.i2p.util.Clock; import net.i2p.util.Clock;
import net.i2p.util.Log; import net.i2p.util.Log;
@@ -43,10 +45,14 @@ import net.i2p.util.OrderedProperties;
* @author jrandom * @author jrandom
*/ */
public class RouterInfo extends DatabaseEntry { public class RouterInfo extends DatabaseEntry {
private final static Log _log = new Log(RouterInfo.class);
private RouterIdentity _identity; private RouterIdentity _identity;
private volatile long _published; private volatile long _published;
private final Set<RouterAddress> _addresses; /**
* Addresses must be sorted by SHA256.
* When an RI is created, they are sorted in setAddresses().
* Save addresses in the order received so we need not resort.
*/
private final List<RouterAddress> _addresses;
/** may be null to save memory, no longer final */ /** may be null to save memory, no longer final */
private Set<Hash> _peers; private Set<Hash> _peers;
private final Properties _options; private final Properties _options;
@@ -71,7 +77,7 @@ public class RouterInfo extends DatabaseEntry {
public static final String BW_CAPABILITY_CHARS = "KLMNO"; public static final String BW_CAPABILITY_CHARS = "KLMNO";
public RouterInfo() { public RouterInfo() {
_addresses = new HashSet(2); _addresses = new ArrayList(2);
_options = new OrderedProperties(); _options = new OrderedProperties();
} }
@@ -156,21 +162,33 @@ public class RouterInfo extends DatabaseEntry {
* *
* @return unmodifiable view, non-null * @return unmodifiable view, non-null
*/ */
public Set<RouterAddress> getAddresses() { public Collection<RouterAddress> getAddresses() {
return Collections.unmodifiableSet(_addresses); return Collections.unmodifiableCollection(_addresses);
} }
/** /**
* Specify a set of RouterAddress structures at which this router * Specify a set of RouterAddress structures at which this router
* can be contacted. * can be contacted.
* *
* @throws IllegalStateException if RouterInfo is already signed * Warning - Sorts the addresses here. Do not modify any address
* after calling this, as the sort order is based on the
* hash of the entire address structure.
*
* @param addresses may be null
* @throws IllegalStateException if RouterInfo is already signed or addresses previously set
*/ */
public void setAddresses(Set<RouterAddress> addresses) { public void setAddresses(Collection<RouterAddress> addresses) {
if (_signature != null) if (_signature != null || !_addresses.isEmpty())
throw new IllegalStateException(); throw new IllegalStateException();
_addresses.clear(); if (addresses != null) {
if (addresses != null) _addresses.addAll(addresses); _addresses.addAll(addresses);
if (_addresses.size() > 1) {
// WARNING this sort algorithm cannot be changed, as it must be consistent
// network-wide. The signature is not checked at readin time, but only
// later, and the addresses are stored in a Set, not a List.
DataHelper.sortStructureList(_addresses);
}
}
} }
/** /**
@@ -270,14 +288,7 @@ public class RouterInfo extends DatabaseEntry {
DataHelper.writeLong(out, 1, 0); DataHelper.writeLong(out, 1, 0);
} else { } else {
DataHelper.writeLong(out, 1, sz); DataHelper.writeLong(out, 1, sz);
Collection<RouterAddress> addresses = _addresses; for (RouterAddress addr : _addresses) {
if (sz > 1) {
// WARNING this sort algorithm cannot be changed, as it must be consistent
// network-wide. The signature is not checked at readin time, but only
// later, and the addresses are stored in a Set, not a List.
addresses = (Collection<RouterAddress>) DataHelper.sortStructures(addresses);
}
for (RouterAddress addr : addresses) {
addr.writeBytes(out); addr.writeBytes(out);
} }
} }
@@ -458,16 +469,16 @@ public class RouterInfo extends DatabaseEntry {
_validated = true; _validated = true;
if (!_isValid) { if (!_isValid) {
Log log = I2PAppContext.getGlobalContext().logManager().getLog(RouterInfo.class);
byte data[] = null; byte data[] = null;
try { try {
data = getBytes(); data = getBytes();
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
_log.error("Error validating", dfe); log.error("Error validating", dfe);
return; return;
} }
if (_log.shouldLog(Log.ERROR)) log.error("Invalid [" + SHA256Generator.getInstance().calculateHash(data).toBase64()
_log.error("Invalid [" + SHA256Generator.getInstance().calculateHash(data).toBase64() + (log.shouldLog(Log.WARN) ? ("]\n" + toString()) : ""),
+ (_log.shouldLog(Log.WARN) ? ("]\n" + toString()) : ""),
new Exception("Signature failed")); new Exception("Signature failed"));
} }
} }

View File

@@ -16,7 +16,7 @@ import net.i2p.I2PAppContext;
/** /**
* Wrapper class for whatever logging system I2P uses. This class should be * Wrapper class for whatever logging system I2P uses. This class should be
* instantiated and kept as a variable for each class it is used by, ala: * instantiated and kept as a variable for each class it is used by, ala:
* <code>private final static Log _log = new Log(MyClassName.class);</code> * <code>private final Log _log = context.logManager().getLog(MyClassName.class);</code>
* *
* If there is anything in here that doesn't make sense, turn off your computer * If there is anything in here that doesn't make sense, turn off your computer
* and go fly a kite. * and go fly a kite.

View File

@@ -31,7 +31,6 @@ public class SimpleTimer2 {
private static final int MIN_THREADS = 2; private static final int MIN_THREADS = 2;
private static final int MAX_THREADS = 4; private static final int MAX_THREADS = 4;
private final I2PAppContext _context; private final I2PAppContext _context;
private static Log _log; // static so TimedEvent can use it
private final ScheduledThreadPoolExecutor _executor; private final ScheduledThreadPoolExecutor _executor;
private final String _name; private final String _name;
private int _count; private int _count;
@@ -40,7 +39,6 @@ public class SimpleTimer2 {
protected SimpleTimer2() { this("SimpleTimer2"); } protected SimpleTimer2() { this("SimpleTimer2"); }
protected SimpleTimer2(String name) { protected SimpleTimer2(String name) {
_context = I2PAppContext.getGlobalContext(); _context = I2PAppContext.getGlobalContext();
_log = _context.logManager().getLog(SimpleTimer2.class);
_name = name; _name = name;
_count = 0; _count = 0;
long maxMemory = Runtime.getRuntime().maxMemory(); long maxMemory = Runtime.getRuntime().maxMemory();
@@ -79,8 +77,10 @@ public class SimpleTimer2 {
@Override @Override
protected void afterExecute(Runnable r, Throwable t) { protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t); super.afterExecute(r, t);
if (t != null) // shoudn't happen, caught in RunnableEvent.run() if (t != null) { // shoudn't happen, caught in RunnableEvent.run()
_log.log(Log.CRIT, "wtf, event borked: " + r, t); Log log = I2PAppContext.getGlobalContext().logManager().getLog(SimpleTimer2.class);
log.log(Log.CRIT, "wtf, event borked: " + r, t);
}
} }
} }
@@ -126,6 +126,7 @@ public class SimpleTimer2 {
* *
*/ */
public static abstract class TimedEvent implements Runnable { public static abstract class TimedEvent implements Runnable {
private final Log _log;
private SimpleTimer2 _pool; private SimpleTimer2 _pool;
private int _fuzz; private int _fuzz;
protected static final int DEFAULT_FUZZ = 3; protected static final int DEFAULT_FUZZ = 3;
@@ -136,7 +137,9 @@ public class SimpleTimer2 {
public TimedEvent(SimpleTimer2 pool) { public TimedEvent(SimpleTimer2 pool) {
_pool = pool; _pool = pool;
_fuzz = DEFAULT_FUZZ; _fuzz = DEFAULT_FUZZ;
_log = I2PAppContext.getGlobalContext().logManager().getLog(SimpleTimer2.class);
} }
/** automatically schedules, don't use this one if you have other things to do first */ /** automatically schedules, don't use this one if you have other things to do first */
public TimedEvent(SimpleTimer2 pool, long timeoutMs) { public TimedEvent(SimpleTimer2 pool, long timeoutMs) {
this(pool); this(pool);

View File

@@ -1,3 +1,36 @@
2012-03-02 zzz
* BlockfileNamingService: Add negative cache
* Build: Add built-by to jars; check for corrupt jars on debug page
* configstats.jsp: Hide log settings unless already enabled
* DataStructures:
- Remove static logs
- Sort addresses in RouterInfo at initialization only;
change from Set to List to save space
- Remove unused counters in Lease to save space
- Increase max leases to 16
* Graphs:
- New single graph page with easy resizing
- Support graphing of previous intervals
* i2pinstall.exe: Add icon
* i2psnark:
- Add tracker configuration form
- Remove custom tracker option from create form
- Add private torrent option
- More icons in buttons
- Use js for refresh
* I2PTunnelHTTPClient:
- Refactoring to use Java URI parser to better handle
escapes, IPv6 addresses, ports
- Rewrite i2paddresshelper scanning/removal
- Refactor out local server code
- Nicer address helper error page
* NetDB:
- Reenable verify of RI stores, disabled in 0.7.9,
checkin comments claim reenabled in 0.7.10 but didn't happen.
- Synchronize StoreJob.sendNext() to avoid dups
* netdb.jsp: Fix debug leaseset count again
* susidns: Add b64 hash to details page
* 2012-02-27 0.8.13 released * 2012-02-27 0.8.13 released
2012-02-22 kytv 2012-02-22 kytv

View File

@@ -5,7 +5,7 @@
<errTitle>I2P Installer</errTitle> <errTitle>I2P Installer</errTitle>
<chdir>.</chdir> <chdir>.</chdir>
<customProcName>false</customProcName> <customProcName>false</customProcName>
<!--<icon>SimpleApp.ico</icon>--> <icon>resources/console.ico</icon>
<jre> <jre>
<minVersion>1.5.0</minVersion> <minVersion>1.5.0</minVersion>
</jre> </jre>

View File

@@ -1,7 +1,7 @@
<launch4jConfig> <launch4jConfig>
<headerType>0</headerType> <headerType>0</headerType>
<jar>../build/launchi2p.jar</jar> <jar>../build/launchi2p.jar</jar>
<outfile>../i2p.exe</outfile> <outfile>../build/i2p.exe</outfile>
<errTitle>I2P</errTitle> <errTitle>I2P</errTitle>
<chdir>.</chdir> <chdir>.</chdir>
<customProcName>false</customProcName> <customProcName>false</customProcName>

View File

@@ -0,0 +1,23 @@
HTTP/1.1 409 Bad Helper
Content-Type: text/html; charset=UTF-8
Cache-control: no-cache
Connection: close
Proxy-Connection: close
<html><head>
<title>I2P Warning: Bad Address Helper</title>
<link rel="shortcut icon" href="http://proxy.i2p/themes/console/images/favicon.ico" >
<link href="http://proxy.i2p/themes/console/default/console.css" rel="stylesheet" type="text/css" >
</head>
<body>
<div class=logo>
<a href="http://127.0.0.1:7657/index.jsp" title="Router Console"><img src="http://proxy.i2p/themes/console/images/i2plogo.png" alt="I2P Router Console" border="0"></a><hr>
<a href="http://127.0.0.1:7657/config.jsp">Configuration</a> <a href="http://127.0.0.1:7657/help.jsp">Help</a> <a href="http://127.0.0.1:7657/susidns/">Addressbook</a>
</div>
<div class=warning id=warning>
<h3>Warning: Bad Address Helper</h3>
<p>
The helper key you put for i2paddresshelper= is not resolvable.
It seems to be garbage data, or a mistyped b32. Check your URL
to try and fix the helper key to be a valid Base 32 hostname or Base 64 key.
</p>

View File

@@ -470,7 +470,7 @@ input {
border: 1px inset #000; border: 1px inset #000;
background: #212 url('/themes/snark/ubergine/images/graytile.png'); background: #212 url('/themes/snark/ubergine/images/graytile.png');
color: #f60; color: #f60;
margin: 2px 0; margin: 2px 4px;
} }
input.r { input.r {
@@ -525,12 +525,50 @@ input[type=radio] {
vertical-align: bottom; vertical-align: bottom;
} }
input.default { width: 1px; height: 1px; visibility: hidden; }
input.accept { input.accept {
background: #989 url('../../console/images/accept.png') no-repeat 2px center; background: #989 url('../../console/images/accept.png') no-repeat 2px center;
padding: 2px 3px 2px 20px !important; padding: 2px 3px 2px 20px !important;
min-height: 22px; min-height: 22px;
} }
input.add {
background: #989 url('../../console/images/add.png') no-repeat 2px center;
padding: 2px 3px 2px 20px !important;
min-height: 22px;
}
input.create {
background: #989 url('images/create.png') no-repeat 2px center;
padding: 2px 3px 2px 20px !important;
min-height: 22px;
}
input.cancel {
background: #989 url('../../console/images/cancel.png') no-repeat 2px center;
padding: 2px 3px 2px 20px !important;
min-height: 22px;
}
input.create {
background: #989 url('images/create.png') no-repeat 2px center;
padding: 2px 3px 2px 20px !important;
min-height: 22px;
}
input.delete {
background: #989 url('../../console/images/delete.png') no-repeat 2px center;
padding: 2px 3px 2px 20px !important;
min-height: 22px;
}
input.reload {
background: #989 url('../../console/images/arrow_refresh.png') no-repeat 2px center;
padding: 2px 3px 2px 20px !important;
min-height: 22px;
}
select { select {
background: #333; background: #333;
background: url('/themes/snark/ubergine/images/graytile.png') !important; background: url('/themes/snark/ubergine/images/graytile.png') !important;

View File

@@ -493,7 +493,7 @@ input {
border: 1px inset #000; border: 1px inset #000;
background: #fff /*url('/themes/snark/ubergine/images/graytile.png')*/; background: #fff /*url('/themes/snark/ubergine/images/graytile.png')*/;
color: #000; color: #000;
margin: 2px 0; margin: 2px 4px;
} }
input.r { input.r {
@@ -549,12 +549,44 @@ input[type=radio] {
vertical-align: bottom; vertical-align: bottom;
} }
input.default { width: 1px; height: 1px; visibility: hidden; }
input.accept { input.accept {
background: #f3efc7 url('../../console/images/accept.png') no-repeat 2px center; background: #f3efc7 url('../../console/images/accept.png') no-repeat 2px center;
padding: 2px 3px 2px 20px !important; padding: 2px 3px 2px 20px !important;
min-height: 22px; min-height: 22px;
} }
input.add {
background: #f3efc7 url('../../console/images/add.png') no-repeat 2px center;
padding: 2px 3px 2px 20px !important;
min-height: 22px;
}
input.cancel {
background: #f3efc7 url('../../console/images/cancel.png') no-repeat 2px center;
padding: 2px 3px 2px 20px !important;
min-height: 22px;
}
input.create {
background: #f3efc7 url('images/create.png') no-repeat 2px center;
padding: 2px 3px 2px 20px !important;
min-height: 22px;
}
input.delete {
background: #f3efc7 url('../../console/images/delete.png') no-repeat 2px center;
padding: 2px 3px 2px 20px !important;
min-height: 22px;
}
input.reload {
background: #f3efc7 url('../../console/images/arrow_refresh.png') no-repeat 2px center;
padding: 2px 3px 2px 20px !important;
min-height: 22px;
}
select { select {
background: #fff; background: #fff;
/* background: url('/themes/snark/ubergine/images/graytile.png') !important;*/ /* background: url('/themes/snark/ubergine/images/graytile.png') !important;*/

View File

@@ -73,6 +73,7 @@
<jar destfile="./build/router.jar" basedir="./build/obj" includes="**/*.class" > <jar destfile="./build/router.jar" basedir="./build/obj" includes="**/*.class" >
<manifest> <manifest>
<attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Implementation-Version" value="${full.version}" />
<attribute name="Built-By" value="${build.built-by}" />
<attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Build-Date" value="${build.timestamp}" />
<attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Base-Revision" value="${workspace.version}" />
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" />

View File

@@ -479,15 +479,9 @@ public class Blocklist {
List<byte[]> rv = new ArrayList(1); List<byte[]> rv = new ArrayList(1);
RouterInfo pinfo = _context.netDb().lookupRouterInfoLocally(peer); RouterInfo pinfo = _context.netDb().lookupRouterInfoLocally(peer);
if (pinfo == null) return rv; if (pinfo == null) return rv;
Set<RouterAddress> paddr = pinfo.getAddresses();
if (paddr == null || paddr.isEmpty())
return rv;
String oldphost = null; String oldphost = null;
List<RouterAddress> pladdr = new ArrayList(paddr);
// for each peer address // for each peer address
for (int j = 0; j < paddr.size(); j++) { for (RouterAddress pa : pinfo.getAddresses()) {
RouterAddress pa = (RouterAddress) pladdr.get(j);
if (pa == null) continue;
String phost = pa.getOption("host"); String phost = pa.getOption("host");
if (phost == null) continue; if (phost == null) continue;
if (oldphost != null && oldphost.equals(phost)) continue; if (oldphost != null && oldphost.equals(phost)) continue;
@@ -787,7 +781,7 @@ public class Blocklist {
Set<Integer> singles = new TreeSet(); Set<Integer> singles = new TreeSet();
singles.addAll(_singleIPBlocklist); singles.addAll(_singleIPBlocklist);
if (!singles.isEmpty()) { if (!singles.isEmpty()) {
out.write("<table><tr><th align=center colspan=2><b>"); out.write("<table><tr><th align=\"center\" colspan=\"2\"><b>");
out.write(_("IPs Banned Until Restart")); out.write(_("IPs Banned Until Restart"));
out.write("</b></td></tr>"); out.write("</b></td></tr>");
// first 0 - 127 // first 0 - 127
@@ -795,27 +789,27 @@ public class Blocklist {
int ip = ii.intValue(); int ip = ii.intValue();
if (ip < 0) if (ip < 0)
continue; continue;
out.write("<tr><td align=center width=50%>"); out.write("<tr><td align=\"center\" width=\"50%\">");
out.write(toStr(ip)); out.write(toStr(ip));
out.write("</td><td width=50%>&nbsp;</td></tr>\n"); out.write("</td><td width=\"50%\">&nbsp;</td></tr>\n");
} }
// then 128 - 255 // then 128 - 255
for (Integer ii : singles) { for (Integer ii : singles) {
int ip = ii.intValue(); int ip = ii.intValue();
if (ip >= 0) if (ip >= 0)
break; break;
out.write("<tr><td align=center width=50%>"); out.write("<tr><td align=\"center\" width=\"50%\">");
out.write(toStr(ip)); out.write(toStr(ip));
out.write("</td><td width=50%>&nbsp;</td></tr>\n"); out.write("</td><td width=\"50%\">&nbsp;</td></tr>\n");
} }
out.write("</table>"); out.write("</table>");
} }
if (_blocklistSize > 0) { if (_blocklistSize > 0) {
out.write("<table><tr><th align=center colspan=2><b>"); out.write("<table><tr><th align=\"center\" colspan=\"2\"><b>");
out.write(_("IPs Permanently Banned")); out.write(_("IPs Permanently Banned"));
out.write("</b></th></tr><tr><td align=center width=50%><b>"); out.write("</b></th></tr><tr><td align=\"center\" width=\"50%\"><b>");
out.write(_("From")); out.write(_("From"));
out.write("</b></td><td align=center width=50%><b>"); out.write("</b></td><td align=\"center\" width=\"50%\"><b>");
out.write(_("To")); out.write(_("To"));
out.write("</b></td></tr>"); out.write("</b></td></tr>");
int max = Math.min(_blocklistSize, MAX_DISPLAY); int max = Math.min(_blocklistSize, MAX_DISPLAY);
@@ -825,7 +819,7 @@ public class Blocklist {
int from = getFrom(_blocklist[i]); int from = getFrom(_blocklist[i]);
if (from < 0) if (from < 0)
continue; continue;
out.write("<tr><td align=center width=50%>"); out.write(toStr(from)); out.write("</td><td align=center width=50%>"); out.write("<tr><td align=\"center\" width=\"50%\">"); out.write(toStr(from)); out.write("</td><td align=\"center\" width=\"50%\">");
int to = getTo(_blocklist[i]); int to = getTo(_blocklist[i]);
if (to != from) { if (to != from) {
out.write(toStr(to)); out.write("</td></tr>\n"); out.write(toStr(to)); out.write("</td></tr>\n");
@@ -838,7 +832,7 @@ public class Blocklist {
int from = getFrom(_blocklist[i]); int from = getFrom(_blocklist[i]);
if (from >= 0) if (from >= 0)
break; break;
out.write("<tr><td align=center width=50%>"); out.write(toStr(from)); out.write("</td><td align=center width=50%>"); out.write("<tr><td align=\"center\" width=\"50%\">"); out.write(toStr(from)); out.write("</td><td align=\"center\" width=\"50%\">");
int to = getTo(_blocklist[i]); int to = getTo(_blocklist[i]);
if (to != from) { if (to != from) {
out.write(toStr(to)); out.write("</td></tr>\n"); out.write(toStr(to)); out.write("</td></tr>\n");

View File

@@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */ /** deprecated */
public final static String ID = "Monotone"; public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION; public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 0; public final static long BUILD = 1;
/** for example "-test" */ /** for example "-test" */
public final static String EXTRA = ""; public final static String EXTRA = "";

View File

@@ -85,7 +85,7 @@ public class StatisticsManager implements Service {
if (_context.getBooleanPropertyDefaultTrue(PROP_PUBLISH_RANKINGS)) { if (_context.getBooleanPropertyDefaultTrue(PROP_PUBLISH_RANKINGS)) {
long publishedUptime = _context.router().getUptime(); long publishedUptime = _context.router().getUptime();
// Don't publish these for first hour // Don't publish these for first hour
if (publishedUptime > 62*60*1000) if (publishedUptime > 62*60*1000 && CoreVersion.VERSION.equals("0.8.13"))
includeAverageThroughput(stats); includeAverageThroughput(stats);
//includeRate("router.invalidMessageTime", stats, new long[] { 10*60*1000 }); //includeRate("router.invalidMessageTime", stats, new long[] { 10*60*1000 });
//includeRate("router.duplicateMessageId", stats, new long[] { 24*60*60*1000 }); //includeRate("router.duplicateMessageId", stats, new long[] { 24*60*60*1000 });

View File

@@ -239,13 +239,13 @@ class ClientConnectionRunner {
} }
/** /**
* Send a DisconnectMessage and log with level Log.CRIT. * Send a DisconnectMessage and log with level Log.ERROR.
* This is always bad. * This is always bad.
* See ClientMessageEventListener.handleCreateSession() * See ClientMessageEventListener.handleCreateSession()
* for why we don't send a SessionStatusMessage when we do this. * for why we don't send a SessionStatusMessage when we do this.
*/ */
void disconnectClient(String reason) { void disconnectClient(String reason) {
disconnectClient(reason, Log.CRIT); disconnectClient(reason, Log.ERROR);
} }
/** /**
@@ -254,9 +254,10 @@ class ClientConnectionRunner {
*/ */
void disconnectClient(String reason, int logLevel) { void disconnectClient(String reason, int logLevel) {
if (_log.shouldLog(logLevel)) if (_log.shouldLog(logLevel))
_log.log(logLevel, "Disconnecting the client (" _log.log(logLevel, "Disconnecting the client - "
+ _config + reason
+ ") : " + reason); + " config: "
+ _config);
DisconnectMessage msg = new DisconnectMessage(); DisconnectMessage msg = new DisconnectMessage();
msg.setReason(reason); msg.setReason(reason);
try { try {

View File

@@ -36,7 +36,7 @@ import net.i2p.util.Log;
* @author jrandom * @author jrandom
*/ */
public class ClientManagerFacadeImpl extends ClientManagerFacade implements InternalClientManager { public class ClientManagerFacadeImpl extends ClientManagerFacade implements InternalClientManager {
private final static Log _log = new Log(ClientManagerFacadeImpl.class); private final Log _log;
private ClientManager _manager; private ClientManager _manager;
private RouterContext _context; private RouterContext _context;
/** note that this is different than the property the client side uses, i2cp.tcp.port */ /** note that this is different than the property the client side uses, i2cp.tcp.port */
@@ -48,7 +48,8 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade implements Inte
public ClientManagerFacadeImpl(RouterContext context) { public ClientManagerFacadeImpl(RouterContext context) {
_context = context; _context = context;
_log.debug("Client manager facade created"); _log = _context.logManager().getLog(ClientManagerFacadeImpl.class);
//_log.debug("Client manager facade created");
} }
public void startup() { public void startup() {

View File

@@ -27,13 +27,13 @@ import net.i2p.util.Log;
* *
*/ */
class RequestLeaseSetJob extends JobImpl { class RequestLeaseSetJob extends JobImpl {
private Log _log; private final Log _log;
private ClientConnectionRunner _runner; private final ClientConnectionRunner _runner;
private LeaseSet _ls; private final LeaseSet _ls;
private long _expiration; private final long _expiration;
private Job _onCreate; private final Job _onCreate;
private Job _onFail; private final Job _onFail;
private LeaseRequestState _requestState; private final LeaseRequestState _requestState;
public RequestLeaseSetJob(RouterContext ctx, ClientConnectionRunner runner, LeaseSet set, long expiration, Job onCreate, Job onFail, LeaseRequestState state) { public RequestLeaseSetJob(RouterContext ctx, ClientConnectionRunner runner, LeaseSet set, long expiration, Job onCreate, Job onFail, LeaseRequestState state) {
super(ctx); super(ctx);
@@ -92,8 +92,8 @@ class RequestLeaseSetJob extends JobImpl {
* *
*/ */
private class CheckLeaseRequestStatus extends JobImpl { private class CheckLeaseRequestStatus extends JobImpl {
private LeaseRequestState _req; private final LeaseRequestState _req;
private long _start; private final long _start;
public CheckLeaseRequestStatus(RouterContext enclosingContext, LeaseRequestState state) { public CheckLeaseRequestStatus(RouterContext enclosingContext, LeaseRequestState state) {
super(enclosingContext); super(enclosingContext);
@@ -114,9 +114,9 @@ class RequestLeaseSetJob extends JobImpl {
return; return;
} else { } else {
RequestLeaseSetJob.CheckLeaseRequestStatus.this.getContext().statManager().addRateData("client.requestLeaseSetTimeout", 1, 0); RequestLeaseSetJob.CheckLeaseRequestStatus.this.getContext().statManager().addRateData("client.requestLeaseSetTimeout", 1, 0);
if (_log.shouldLog(Log.CRIT)) { if (_log.shouldLog(Log.ERROR)) {
long waited = System.currentTimeMillis() - _start; long waited = System.currentTimeMillis() - _start;
_log.log(Log.CRIT, "Failed to receive a leaseSet in the time allotted (" + waited + "): " + _req + " for " _log.error("Failed to receive a leaseSet in the time allotted (" + waited + "): " + _req + " for "
+ _runner.getConfig().getDestination().calculateHash().toBase64()); + _runner.getConfig().getDestination().calculateHash().toBase64());
} }
_runner.disconnectClient("Took too long to request leaseSet"); _runner.disconnectClient("Took too long to request leaseSet");

Some files were not shown because too many files have changed in this diff Show More