forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p.zzz.winfix095' (head 7d31b90b87adb2c0cfb837e5b66cc4c223766331)
to branch 'i2p.i2p' (head b004014ccfbca6241a090d5b47f1228702f4dfcc)
This commit is contained in:
@@ -177,10 +177,11 @@ Applications:
|
||||
By welterde.
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
Jetty 6.1.26:
|
||||
Copyright 1995-2009 Mort Bay Consulting Pty Ltd
|
||||
See licenses/LICENSE-Jetty.txt
|
||||
Jetty 7.6.10.v20130312:
|
||||
See licenses/ABOUT-Jetty.html
|
||||
See licenses/NOTICE-Jetty.html
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
See licenses/LICENSE-ECLIPSE-1.0.html
|
||||
See licenses/NOTICE-Commons-Logging.txt
|
||||
|
||||
JRobin 1.5.9.1:
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<pathelement location="../../ministreaming/java/build/obj" />
|
||||
<pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
|
||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-servlet.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-util.jar" />
|
||||
</classpath>
|
||||
</depend>
|
||||
@@ -38,7 +39,7 @@
|
||||
debug="true" deprecation="on" source="1.5" target="1.5"
|
||||
destdir="./build/obj"
|
||||
includeAntRuntime="false"
|
||||
classpath="../../../core/java/build/i2p.jar:../../jetty/jettylib/org.mortbay.jetty.jar:../../jetty/jettylib/javax.servlet.jar:../../jetty/jettylib/jetty-util.jar:../../ministreaming/java/build/mstreaming.jar" >
|
||||
classpath="../../../core/java/build/i2p.jar:../../jetty/jettylib/org.mortbay.jetty.jar:../../jetty/jettylib/javax.servlet.jar:../../jetty/jettylib/jetty-servlet.jar:../../jetty/jettylib/jetty-util.jar:../../ministreaming/java/build/mstreaming.jar" >
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
</javac>
|
||||
</target>
|
||||
@@ -60,7 +61,7 @@
|
||||
<target name="jar" depends="builddep, compile, jarUpToDate, listChangedFiles" unless="jar.uptodate" >
|
||||
<!-- set if unset -->
|
||||
<property name="workspace.changes.tr" value="" />
|
||||
<jar destfile="./build/i2psnark.jar" basedir="./build/obj" includes="**/*.class" excludes="**/I2PSnarkServlet*.class **/FetchAndAdd*.class **/messages_*.class">
|
||||
<jar destfile="./build/i2psnark.jar" basedir="./build/obj" includes="**/*.class" excludes="**/web/* **/messages_*.class">
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="org.klomp.snark.Snark" />
|
||||
<attribute name="Class-Path" value="i2p.jar mstreaming.jar streaming.jar" />
|
||||
@@ -75,7 +76,7 @@
|
||||
|
||||
<target name="jarUpToDate">
|
||||
<uptodate property="jar.uptodate" targetfile="build/i2psnark.jar" >
|
||||
<srcfiles dir= "build/obj" includes="**/*.class" excludes="**/I2PSnarkServlet*.class **/FetchAndAdd*.class **/messages_*.class" />
|
||||
<srcfiles dir= "build/obj" includes="**/*.class" excludes="**/web/* **/messages_*.class" />
|
||||
</uptodate>
|
||||
<condition property="shouldListChanges" >
|
||||
<and>
|
||||
@@ -103,9 +104,11 @@
|
||||
<copy todir="build/icons/.icons" >
|
||||
<fileset dir="../icons/" />
|
||||
</copy>
|
||||
<!-- mime.properties must be in with the classes -->
|
||||
<copy file="../mime.properties" todir="build/obj/org/klomp/snark/web" />
|
||||
<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 -->
|
||||
<classes dir="./build/obj" includes="**/web/*.class" />
|
||||
<classes dir="./build/obj" includes="**/web/*" />
|
||||
<fileset dir="build/icons/" />
|
||||
<manifest>
|
||||
<attribute name="Implementation-Version" value="${full.version}" />
|
||||
|
||||
@@ -49,6 +49,7 @@ import org.klomp.snark.dht.KRPC;
|
||||
public class I2PSnarkUtil {
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
private final String _baseName;
|
||||
|
||||
private boolean _shouldProxy;
|
||||
private String _proxyHost;
|
||||
@@ -82,8 +83,17 @@ public class I2PSnarkUtil {
|
||||
public static final boolean DEFAULT_USE_DHT = true;
|
||||
|
||||
public I2PSnarkUtil(I2PAppContext ctx) {
|
||||
this(ctx, "i2psnark");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param baseName generally "i2psnark"
|
||||
* @since Jetty 7
|
||||
*/
|
||||
public I2PSnarkUtil(I2PAppContext ctx, String baseName) {
|
||||
_context = ctx;
|
||||
_log = _context.logManager().getLog(Snark.class);
|
||||
_baseName = baseName;
|
||||
_opts = new HashMap();
|
||||
//setProxy("127.0.0.1", 4444);
|
||||
setI2CPConfig("127.0.0.1", 7654, null);
|
||||
@@ -99,7 +109,7 @@ public class I2PSnarkUtil {
|
||||
// This is used for both announce replies and .torrent file downloads,
|
||||
// so it must be available even if not connected to I2CP.
|
||||
// so much for multiple instances
|
||||
_tmpDir = new SecureDirectory(ctx.getTempDir(), "i2psnark");
|
||||
_tmpDir = new SecureDirectory(ctx.getTempDir(), baseName);
|
||||
FileUtil.rmdir(_tmpDir, false);
|
||||
_tmpDir.mkdirs();
|
||||
}
|
||||
@@ -216,9 +226,9 @@ public class I2PSnarkUtil {
|
||||
}
|
||||
}
|
||||
if (opts.getProperty("inbound.nickname") == null)
|
||||
opts.setProperty("inbound.nickname", "I2PSnark");
|
||||
opts.setProperty("inbound.nickname", _baseName.replace("i2psnark", "I2PSnark"));
|
||||
if (opts.getProperty("outbound.nickname") == null)
|
||||
opts.setProperty("outbound.nickname", "I2PSnark");
|
||||
opts.setProperty("outbound.nickname", _baseName.replace("i2psnark", "I2PSnark"));
|
||||
if (opts.getProperty("outbound.priority") == null)
|
||||
opts.setProperty("outbound.priority", "-10");
|
||||
// Dont do this for now, it is set in I2PSocketEepGet for announces,
|
||||
@@ -253,7 +263,7 @@ public class I2PSnarkUtil {
|
||||
_connecting = false;
|
||||
}
|
||||
if (_shouldUseDHT && _manager != null && _dht == null)
|
||||
_dht = new KRPC(_context, _manager.getSession());
|
||||
_dht = new KRPC(_context, _baseName, _manager.getSession());
|
||||
return (_manager != null);
|
||||
}
|
||||
|
||||
@@ -588,7 +598,7 @@ public class I2PSnarkUtil {
|
||||
public synchronized void setUseDHT(boolean yes) {
|
||||
_shouldUseDHT = yes;
|
||||
if (yes && _manager != null && _dht == null) {
|
||||
_dht = new KRPC(_context, _manager.getSession());
|
||||
_dht = new KRPC(_context, _baseName, _manager.getSession());
|
||||
} else if (!yes && _dht != null) {
|
||||
_dht.stop();
|
||||
_dht = null;
|
||||
|
||||
@@ -359,7 +359,7 @@ public class Peer implements Comparable
|
||||
String bittorrentProtocol = new String(bs, "UTF-8");
|
||||
if (!"BitTorrent protocol".equals(bittorrentProtocol))
|
||||
throw new IOException("Handshake failure, expected "
|
||||
+ "'Bittorrent protocol', got '"
|
||||
+ "'BitTorrent protocol', got '"
|
||||
+ bittorrentProtocol + "'");
|
||||
|
||||
// Handshake read - options
|
||||
|
||||
@@ -57,6 +57,8 @@ public class SnarkManager implements CompleteListener {
|
||||
private /* FIXME final FIXME */ File _configFile;
|
||||
private Properties _config;
|
||||
private final I2PAppContext _context;
|
||||
private final String _contextPath;
|
||||
private final String _contextName;
|
||||
private final Log _log;
|
||||
private final Queue<String> _messages;
|
||||
private final I2PSnarkUtil _util;
|
||||
@@ -82,7 +84,7 @@ public class SnarkManager implements CompleteListener {
|
||||
public static final String PROP_META_PRIORITY_SUFFIX = ".priority";
|
||||
public static final String PROP_META_MAGNET_PREFIX = "i2psnark.magnet.";
|
||||
|
||||
private static final String CONFIG_FILE = "i2psnark.config";
|
||||
private static final String CONFIG_FILE_SUFFIX = ".config";
|
||||
public static final String PROP_FILES_PUBLIC = "i2psnark.filesPublic";
|
||||
public static final String PROP_AUTO_START = "i2snark.autoStart"; // oops
|
||||
public static final String DEFAULT_AUTO_START = "false";
|
||||
@@ -90,6 +92,7 @@ public class SnarkManager implements CompleteListener {
|
||||
//public static final String DEFAULT_LINK_PREFIX = "file:///";
|
||||
public static final String PROP_STARTUP_DELAY = "i2psnark.startupDelay";
|
||||
public static final String PROP_REFRESH_DELAY = "i2psnark.refreshSeconds";
|
||||
public static final String PROP_PAGE_SIZE = "i2psnark.pageSize";
|
||||
public static final String RC_PROP_THEME = "routerconsole.theme";
|
||||
public static final String RC_PROP_UNIVERSAL_THEMING = "routerconsole.universal.theme";
|
||||
public static final String PROP_THEME = "i2psnark.theme";
|
||||
@@ -103,6 +106,7 @@ public class SnarkManager implements CompleteListener {
|
||||
public static final int DEFAULT_MAX_UP_BW = 10;
|
||||
public static final int DEFAULT_STARTUP_DELAY = 3;
|
||||
public static final int DEFAULT_REFRESH_DELAY_SECS = 60;
|
||||
private static final int DEFAULT_PAGE_SIZE = 50;
|
||||
|
||||
/**
|
||||
* "name", "announceURL=websiteURL" pairs
|
||||
@@ -128,17 +132,33 @@ public class SnarkManager implements CompleteListener {
|
||||
/** comma delimited list of name=announceURL=baseURL for the trackers to be displayed */
|
||||
public static final String PROP_TRACKERS = "i2psnark.trackers";
|
||||
|
||||
/**
|
||||
* For embedded.
|
||||
*/
|
||||
public SnarkManager(I2PAppContext ctx) {
|
||||
this(ctx, "/i2psnark", "i2psnark");
|
||||
}
|
||||
|
||||
/**
|
||||
* For webapp.
|
||||
* @param ctxPath generally "/i2psnark"
|
||||
* @param ctxName generally "i2psnark"
|
||||
* @since 0.9.6
|
||||
*/
|
||||
public SnarkManager(I2PAppContext ctx, String ctxPath, String ctxName) {
|
||||
_snarks = new ConcurrentHashMap();
|
||||
_magnets = new ConcurrentHashSet();
|
||||
_addSnarkLock = new Object();
|
||||
_context = ctx;
|
||||
_contextPath = ctxPath;
|
||||
_contextName = ctxName;
|
||||
_log = _context.logManager().getLog(SnarkManager.class);
|
||||
_messages = new LinkedBlockingQueue();
|
||||
_util = new I2PSnarkUtil(_context);
|
||||
_configFile = new File(CONFIG_FILE);
|
||||
_util = new I2PSnarkUtil(_context, ctxName);
|
||||
String cfile = ctxName + CONFIG_FILE_SUFFIX;
|
||||
_configFile = new File(cfile);
|
||||
if (!_configFile.isAbsolute())
|
||||
_configFile = new File(_context.getConfigDir(), CONFIG_FILE);
|
||||
_configFile = new File(_context.getConfigDir(), cfile);
|
||||
_trackerMap = new ConcurrentHashMap(4);
|
||||
loadConfig(null);
|
||||
}
|
||||
@@ -152,8 +172,10 @@ public class SnarkManager implements CompleteListener {
|
||||
_connectionAcceptor = new ConnectionAcceptor(_util);
|
||||
_monitor = new I2PAppThread(new DirMonitor(), "Snark DirMonitor", true);
|
||||
_monitor.start();
|
||||
// delay until UpdateManager is there
|
||||
_context.simpleScheduler().addEvent(new Register(), 4*60*1000);
|
||||
// only if default instance
|
||||
if ("i2psnark".equals(_contextName))
|
||||
// delay until UpdateManager is there
|
||||
_context.simpleScheduler().addEvent(new Register(), 4*60*1000);
|
||||
// Not required, Jetty has a shutdown hook
|
||||
//_context.addShutdownTask(new SnarkManagerShutdown());
|
||||
}
|
||||
@@ -250,6 +272,18 @@ public class SnarkManager implements CompleteListener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For GUI
|
||||
* @since 0.9.6
|
||||
*/
|
||||
public int getPageSize() {
|
||||
try {
|
||||
return Integer.parseInt(_config.getProperty(PROP_PAGE_SIZE));
|
||||
} catch (NumberFormatException nfe) {
|
||||
return DEFAULT_PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
private int getStartupDelayMinutes() {
|
||||
try {
|
||||
return Integer.parseInt(_config.getProperty(PROP_STARTUP_DELAY));
|
||||
@@ -259,7 +293,7 @@ public class SnarkManager implements CompleteListener {
|
||||
}
|
||||
|
||||
public File getDataDir() {
|
||||
String dir = _config.getProperty(PROP_DIR, "i2psnark");
|
||||
String dir = _config.getProperty(PROP_DIR, _contextName);
|
||||
File f;
|
||||
if (areFilesPublic())
|
||||
f = new File(dir);
|
||||
@@ -305,13 +339,15 @@ public class SnarkManager implements CompleteListener {
|
||||
if (!_config.containsKey(PROP_UPLOADERS_TOTAL))
|
||||
_config.setProperty(PROP_UPLOADERS_TOTAL, "" + Snark.MAX_TOTAL_UPLOADERS);
|
||||
if (!_config.containsKey(PROP_DIR))
|
||||
_config.setProperty(PROP_DIR, "i2psnark");
|
||||
_config.setProperty(PROP_DIR, _contextName);
|
||||
if (!_config.containsKey(PROP_AUTO_START))
|
||||
_config.setProperty(PROP_AUTO_START, DEFAULT_AUTO_START);
|
||||
if (!_config.containsKey(PROP_REFRESH_DELAY))
|
||||
_config.setProperty(PROP_REFRESH_DELAY, Integer.toString(DEFAULT_REFRESH_DELAY_SECS));
|
||||
if (!_config.containsKey(PROP_STARTUP_DELAY))
|
||||
_config.setProperty(PROP_STARTUP_DELAY, Integer.toString(DEFAULT_STARTUP_DELAY));
|
||||
if (!_config.containsKey(PROP_PAGE_SIZE))
|
||||
_config.setProperty(PROP_PAGE_SIZE, Integer.toString(DEFAULT_PAGE_SIZE));
|
||||
if (!_config.containsKey(PROP_THEME))
|
||||
_config.setProperty(PROP_THEME, DEFAULT_THEME);
|
||||
// no, so we can switch default to true later
|
||||
@@ -429,11 +465,15 @@ public class SnarkManager implements CompleteListener {
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* all params may be null or need trimming
|
||||
*/
|
||||
public void updateConfig(String dataDir, boolean filesPublic, boolean autoStart, String refreshDelay,
|
||||
String startDelay, String seedPct, String eepHost,
|
||||
String startDelay, String pageSize, String seedPct, String eepHost,
|
||||
String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
|
||||
String upLimit, String upBW, boolean useOpenTrackers, boolean useDHT, String theme) {
|
||||
boolean changed = false;
|
||||
boolean interruptMonitor = false;
|
||||
//if (eepHost != null) {
|
||||
// // unused, we use socket eepget
|
||||
// int port = _util.getEepProxyPort();
|
||||
@@ -450,12 +490,12 @@ public class SnarkManager implements CompleteListener {
|
||||
//}
|
||||
if (upLimit != null) {
|
||||
int limit = _util.getMaxUploaders();
|
||||
try { limit = Integer.parseInt(upLimit); } catch (NumberFormatException nfe) {}
|
||||
try { limit = Integer.parseInt(upLimit.trim()); } catch (NumberFormatException nfe) {}
|
||||
if ( limit != _util.getMaxUploaders()) {
|
||||
if ( limit >= Snark.MIN_TOTAL_UPLOADERS ) {
|
||||
_util.setMaxUploaders(limit);
|
||||
changed = true;
|
||||
_config.setProperty(PROP_UPLOADERS_TOTAL, "" + limit);
|
||||
_config.setProperty(PROP_UPLOADERS_TOTAL, Integer.toString(limit));
|
||||
addMessage(_("Total uploaders limit changed to {0}", limit));
|
||||
} else {
|
||||
addMessage(_("Minimum total uploaders limit is {0}", Snark.MIN_TOTAL_UPLOADERS));
|
||||
@@ -464,12 +504,12 @@ public class SnarkManager implements CompleteListener {
|
||||
}
|
||||
if (upBW != null) {
|
||||
int limit = _util.getMaxUpBW();
|
||||
try { limit = Integer.parseInt(upBW); } catch (NumberFormatException nfe) {}
|
||||
try { limit = Integer.parseInt(upBW.trim()); } catch (NumberFormatException nfe) {}
|
||||
if ( limit != _util.getMaxUpBW()) {
|
||||
if ( limit >= MIN_UP_BW ) {
|
||||
_util.setMaxUpBW(limit);
|
||||
changed = true;
|
||||
_config.setProperty(PROP_UPBW_MAX, "" + limit);
|
||||
_config.setProperty(PROP_UPBW_MAX, Integer.toString(limit));
|
||||
addMessage(_("Up BW limit changed to {0}KBps", limit));
|
||||
} else {
|
||||
addMessage(_("Minimum up bandwidth limit is {0}KBps", MIN_UP_BW));
|
||||
@@ -479,21 +519,21 @@ public class SnarkManager implements CompleteListener {
|
||||
|
||||
if (startDelay != null){
|
||||
int minutes = _util.getStartupDelay();
|
||||
try { minutes = Integer.parseInt(startDelay); } catch (NumberFormatException nfe) {}
|
||||
try { minutes = Integer.parseInt(startDelay.trim()); } catch (NumberFormatException nfe) {}
|
||||
if ( minutes != _util.getStartupDelay()) {
|
||||
_util.setStartupDelay(minutes);
|
||||
changed = true;
|
||||
_config.setProperty(PROP_STARTUP_DELAY, "" + minutes);
|
||||
_config.setProperty(PROP_STARTUP_DELAY, Integer.toString(minutes));
|
||||
addMessage(_("Startup delay changed to {0}", DataHelper.formatDuration2(minutes * 60 * 1000)));
|
||||
}
|
||||
}
|
||||
|
||||
if (refreshDelay != null) {
|
||||
try {
|
||||
int secs = Integer.parseInt(refreshDelay);
|
||||
int secs = Integer.parseInt(refreshDelay.trim());
|
||||
if (secs != getRefreshDelaySeconds()) {
|
||||
changed = true;
|
||||
_config.setProperty(PROP_REFRESH_DELAY, refreshDelay);
|
||||
_config.setProperty(PROP_REFRESH_DELAY, Integer.toString(secs));
|
||||
if (secs >= 0)
|
||||
addMessage(_("Refresh time changed to {0}", DataHelper.formatDuration2(secs * 1000)));
|
||||
else
|
||||
@@ -502,6 +542,40 @@ public class SnarkManager implements CompleteListener {
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
|
||||
if (pageSize != null) {
|
||||
try {
|
||||
int size = Integer.parseInt(pageSize.trim());
|
||||
if (size <= 0)
|
||||
size = 999999;
|
||||
if (size != getPageSize() && size >= 5) {
|
||||
changed = true;
|
||||
pageSize = Integer.toString(size);
|
||||
_config.setProperty(PROP_PAGE_SIZE, pageSize);
|
||||
addMessage(_("Page size changed to {0}", pageSize));
|
||||
}
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
|
||||
if (dataDir != null && !dataDir.equals(getDataDir().getAbsolutePath())) {
|
||||
dataDir = dataDir.trim();
|
||||
File dd = new File(dataDir);
|
||||
if (!dd.isAbsolute()) {
|
||||
addMessage(_("Data directory must be an absolute path") + ": " + dataDir);
|
||||
} else if (!dd.exists()) {
|
||||
addMessage(_("Data directory does not exist") + ": " + dataDir);
|
||||
} else if (!dd.isDirectory()) {
|
||||
addMessage(_("Not a directory") + ": " + dataDir);
|
||||
} else if (!dd.canRead()) {
|
||||
addMessage(_("Unreadable") + ": " + dataDir);
|
||||
} else {
|
||||
changed = true;
|
||||
interruptMonitor = true;
|
||||
_config.setProperty(PROP_DIR, dataDir);
|
||||
addMessage(_("Data directory changed to {0}", dataDir));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Start of I2CP stuff.
|
||||
// i2cpHost will generally be null since it is hidden from the form if in router context.
|
||||
|
||||
@@ -636,6 +710,9 @@ public class SnarkManager implements CompleteListener {
|
||||
}
|
||||
if (changed) {
|
||||
saveConfig();
|
||||
if (interruptMonitor)
|
||||
// Data dir changed. this will stop and remove all old torrents, and add the new ones
|
||||
_monitor.interrupt();
|
||||
} else {
|
||||
addMessage(_("Configuration unchanged."));
|
||||
}
|
||||
@@ -731,6 +808,11 @@ public class SnarkManager implements CompleteListener {
|
||||
|
||||
public Properties getConfig() { return _config; }
|
||||
|
||||
/** @since Jetty 7 */
|
||||
public String getConfigFilename() {
|
||||
return _configFile.getAbsolutePath();
|
||||
}
|
||||
|
||||
/** hardcoded for sanity. perhaps this should be customizable, for people who increase their ulimit, etc. */
|
||||
public static final int MAX_FILES_PER_TORRENT = 512;
|
||||
|
||||
@@ -1445,7 +1527,7 @@ public class SnarkManager implements CompleteListener {
|
||||
if (meta == null || storage == null)
|
||||
return;
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
buf.append("<a href=\"/i2psnark/").append(storage.getBaseName());
|
||||
buf.append("<a href=\"").append(_contextPath).append('/').append(storage.getBaseName());
|
||||
if (meta.getFiles() != null)
|
||||
buf.append('/');
|
||||
buf.append("\">").append(storage.getBaseName()).append("</a>");
|
||||
|
||||
@@ -114,6 +114,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
/** signed dgrams */
|
||||
private final int _qPort;
|
||||
private final File _dhtFile;
|
||||
private final File _backupDhtFile;
|
||||
private volatile boolean _isRunning;
|
||||
private volatile boolean _hasBootstrapped;
|
||||
/** stats */
|
||||
@@ -152,12 +153,15 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
private static final long CLEAN_TIME = 63*1000;
|
||||
private static final long EXPLORE_TIME = 877*1000;
|
||||
private static final long BLACKLIST_CLEAN_TIME = 17*60*1000;
|
||||
private static final String DHT_FILE = "i2psnark.dht.dat";
|
||||
private static final String DHT_FILE_SUFFIX = ".dht.dat";
|
||||
|
||||
private static final int SEND_CRYPTO_TAGS = 8;
|
||||
private static final int LOW_CRYPTO_TAGS = 4;
|
||||
|
||||
public KRPC (I2PAppContext ctx, I2PSession session) {
|
||||
/**
|
||||
* @param baseName generally "i2psnark"
|
||||
*/
|
||||
public KRPC(I2PAppContext ctx, String baseName, I2PSession session) {
|
||||
_context = ctx;
|
||||
_session = session;
|
||||
_log = ctx.logManager().getLog(KRPC.class);
|
||||
@@ -182,7 +186,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
_myNID = new NID(_myID);
|
||||
}
|
||||
_myNodeInfo = new NodeInfo(_myNID, session.getMyDestination(), _qPort);
|
||||
_dhtFile = new File(ctx.getConfigDir(), DHT_FILE);
|
||||
_dhtFile = new File(ctx.getConfigDir(), baseName + DHT_FILE_SUFFIX);
|
||||
_backupDhtFile = baseName.equals("i2psnark") ? null : new File(ctx.getConfigDir(), "i2psnark" + DHT_FILE_SUFFIX);
|
||||
_knownNodes = new DHTNodes(ctx, _myNID);
|
||||
|
||||
start();
|
||||
@@ -547,7 +552,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
_session.addMuxedSessionListener(this, I2PSession.PROTO_DATAGRAM, _qPort);
|
||||
_knownNodes.start();
|
||||
_tracker.start();
|
||||
PersistDHT.loadDHT(this, _dhtFile);
|
||||
PersistDHT.loadDHT(this, _dhtFile, _backupDhtFile);
|
||||
// start the explore thread
|
||||
_isRunning = true;
|
||||
// no need to keep ref, it will eventually stop
|
||||
|
||||
@@ -23,6 +23,17 @@ abstract class PersistDHT {
|
||||
|
||||
private static final long MAX_AGE = 60*60*1000;
|
||||
|
||||
/**
|
||||
* @param backupFile may be null
|
||||
* @since 0.9.6
|
||||
*/
|
||||
public static synchronized void loadDHT(KRPC krpc, File file, File backupFile) {
|
||||
if (file.exists())
|
||||
loadDHT(krpc, file);
|
||||
else if (backupFile != null)
|
||||
loadDHT(krpc, backupFile);
|
||||
}
|
||||
|
||||
public static synchronized void loadDHT(KRPC krpc, File file) {
|
||||
Log log = I2PAppContext.getGlobalContext().logManager().getLog(PersistDHT.class);
|
||||
int count = 0;
|
||||
|
||||
550
apps/i2psnark/java/src/org/klomp/snark/web/BasicServlet.java
Normal file
550
apps/i2psnark/java/src/org/klomp/snark/web/BasicServlet.java
Normal file
@@ -0,0 +1,550 @@
|
||||
// ========================================================================
|
||||
// Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ========================================================================
|
||||
|
||||
package org.klomp.snark.web;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.UnavailableException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.util.ByteCache;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Based on DefaultServlet from Jetty 6.1.26, heavily simplified
|
||||
* and modified to remove all dependencies on Jetty libs.
|
||||
*
|
||||
* Supports HEAD and GET only, for resources from the .war and local files.
|
||||
* Supports files and resource only.
|
||||
* Supports MIME types with local overrides and additions.
|
||||
* Supports Last-Modified.
|
||||
*
|
||||
* Does not support directories or "welcome files".
|
||||
* Does not support gzip.
|
||||
* Does not support request ranges.
|
||||
* Does not cache.
|
||||
*
|
||||
* HEAD and POST return 405.
|
||||
* Directories return 403.
|
||||
* Jar resources are sent with a long cache directive.
|
||||
*
|
||||
* ------------------------------------------------------------
|
||||
*
|
||||
* The default servlet.
|
||||
* This servlet, normally mapped to /, provides the handling for static
|
||||
* content, OPTION and TRACE methods for the context.
|
||||
* The following initParameters are supported, these can be set either
|
||||
* on the servlet itself or as ServletContext initParameters with a prefix
|
||||
* of org.mortbay.jetty.servlet.Default. :
|
||||
* <PRE>
|
||||
*
|
||||
* resourceBase Set to replace the context resource base
|
||||
|
||||
* warBase Path allowed for resource in war
|
||||
*
|
||||
* </PRE>
|
||||
*
|
||||
*
|
||||
* @author Greg Wilkins (gregw)
|
||||
* @author Nigel Canonizado
|
||||
*
|
||||
* @since Jetty 7
|
||||
*/
|
||||
class BasicServlet extends HttpServlet
|
||||
{
|
||||
protected final I2PAppContext _context;
|
||||
protected final Log _log;
|
||||
protected File _resourceBase;
|
||||
private String _warBase;
|
||||
|
||||
private final MimeTypes _mimeTypes;
|
||||
|
||||
/** same as PeerState.PARTSIZE */
|
||||
private static final int BUFSIZE = 16*1024;
|
||||
private ByteCache _cache = ByteCache.getInstance(16, BUFSIZE);
|
||||
|
||||
private static final int WAR_CACHE_CONTROL_SECS = 24*60*60;
|
||||
private static final int FILE_CACHE_CONTROL_SECS = 24*60*60;
|
||||
|
||||
public BasicServlet() {
|
||||
super();
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_log = _context.logManager().getLog(getClass());
|
||||
_mimeTypes = new MimeTypes();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void init(ServletConfig cfg) throws ServletException {
|
||||
super.init(cfg);
|
||||
String rb=getInitParameter("resourceBase");
|
||||
if (rb!=null)
|
||||
{
|
||||
File f = new File(rb);
|
||||
setResourceBase(f);
|
||||
}
|
||||
String wb = getInitParameter("warBase");
|
||||
if (wb != null)
|
||||
setWarBase(wb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Files are served from here
|
||||
*/
|
||||
protected void setResourceBase(File base) throws UnavailableException {
|
||||
if (!base.isDirectory())
|
||||
throw new UnavailableException("Resource base does not exist: " + base);
|
||||
_resourceBase = base;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Resource base is " + _resourceBase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only paths starting with this in the path are served
|
||||
*/
|
||||
protected void setWarBase(String base) {
|
||||
if (!base.startsWith("/"))
|
||||
base = '/' + base;
|
||||
if (!base.endsWith("/"))
|
||||
base = base + '/';
|
||||
_warBase = base;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("War base is " + _warBase);
|
||||
}
|
||||
|
||||
/** get Resource to serve.
|
||||
* Map a path to a resource. The default implementation calls
|
||||
* HttpContext.getResource but derived servlets may provide
|
||||
* their own mapping.
|
||||
* @param pathInContext The path to find a resource for.
|
||||
* @return The resource to serve or null if not existing
|
||||
*/
|
||||
public File getResource(String pathInContext)
|
||||
{
|
||||
if (_resourceBase==null)
|
||||
return null;
|
||||
File r = null;
|
||||
if (!pathInContext.contains("..") &&
|
||||
!pathInContext.endsWith("/")) {
|
||||
File f = new File(_resourceBase, pathInContext);
|
||||
if (f.exists())
|
||||
r = f;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/** get Resource to serve.
|
||||
* Map a path to a resource. The default implementation calls
|
||||
* HttpContext.getResource but derived servlets may provide
|
||||
* their own mapping.
|
||||
* @param pathInContext The path to find a resource for.
|
||||
* @return The resource to serve or null. Returns null for directories
|
||||
*/
|
||||
public HttpContent getContent(String pathInContext)
|
||||
{
|
||||
if (_resourceBase==null)
|
||||
return null;
|
||||
HttpContent r = null;
|
||||
if (_warBase != null && pathInContext.startsWith(_warBase)) {
|
||||
r = new JarContent(pathInContext);
|
||||
} else if (!pathInContext.contains("..") &&
|
||||
!pathInContext.endsWith("/")) {
|
||||
File f = new File(_resourceBase, pathInContext);
|
||||
// exists && !directory
|
||||
if (f.isFile())
|
||||
r = new FileContent(f);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
// always starts with a '/'
|
||||
String servletpath = request.getServletPath();
|
||||
String pathInfo=request.getPathInfo();
|
||||
// ??? right??
|
||||
String pathInContext = addPaths(servletpath, pathInfo);
|
||||
|
||||
// Find the resource and content
|
||||
try {
|
||||
HttpContent content = getContent(pathInContext);
|
||||
|
||||
// Handle resource
|
||||
if (content == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Not found: " + pathInContext);
|
||||
response.sendError(404);
|
||||
} else {
|
||||
if (passConditionalHeaders(request, response, content)) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Sending: " + content);
|
||||
sendData(request, response, content);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Not modified: " + content);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(IllegalArgumentException e)
|
||||
{
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error sending " + pathInContext, e);
|
||||
if(!response.isCommitted())
|
||||
response.sendError(500, e.getMessage());
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
// typical browser abort
|
||||
//_log.warn("Error sending", e);
|
||||
_log.warn("Error sending " + pathInContext + ": " + e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
response.sendError(405);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* (non-Javadoc)
|
||||
* @see javax.servlet.http.HttpServlet#doTrace(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||
*/
|
||||
protected void doTrace(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
response.sendError(405);
|
||||
}
|
||||
|
||||
protected void doOptions(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
response.sendError(405);
|
||||
}
|
||||
|
||||
protected void doDelete(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
response.sendError(405);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Check modification date headers.
|
||||
* @return true to keep going, false if handled here
|
||||
*/
|
||||
protected boolean passConditionalHeaders(HttpServletRequest request,HttpServletResponse response, HttpContent content)
|
||||
throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!request.getMethod().equals("HEAD") ) {
|
||||
long ifmsl=request.getDateHeader("If-Modified-Since");
|
||||
if (ifmsl!=-1)
|
||||
{
|
||||
if (content.getLastModified()/1000 <= ifmsl/1000)
|
||||
{
|
||||
response.reset();
|
||||
response.setStatus(304);
|
||||
response.flushBuffer();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(IllegalArgumentException iae)
|
||||
{
|
||||
if(!response.isCommitted())
|
||||
response.sendError(400, iae.getMessage());
|
||||
throw iae;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void sendData(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
HttpContent content)
|
||||
throws IOException
|
||||
{
|
||||
InputStream in =null;
|
||||
try {
|
||||
in = content.getInputStream();
|
||||
} catch (IOException e) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Not found: " + content);
|
||||
response.sendError(404);
|
||||
return;
|
||||
}
|
||||
|
||||
OutputStream out =null;
|
||||
try {
|
||||
out = response.getOutputStream();
|
||||
} catch (IllegalStateException e) {
|
||||
out = new WriterOutputStream(response.getWriter());
|
||||
}
|
||||
|
||||
// Write content normally
|
||||
long content_length = content.getContentLength();
|
||||
writeHeaders(response,content,content_length);
|
||||
if (content_length >= 0 && request.getMethod().equals("HEAD")) {
|
||||
// if we know the content length, don't send it to be counted
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("HEAD: " + content);
|
||||
} else {
|
||||
// GET or unknown size for HEAD
|
||||
copy(in, out);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void writeHeaders(HttpServletResponse response,HttpContent content,long count)
|
||||
throws IOException
|
||||
{
|
||||
if (content.getContentType()!=null && response.getContentType()==null)
|
||||
response.setContentType(content.getContentType());
|
||||
|
||||
long lml = content.getLastModified();
|
||||
if (lml > 0)
|
||||
response.setDateHeader("Last-Modified",lml);
|
||||
|
||||
if (count != -1)
|
||||
{
|
||||
if (count<Integer.MAX_VALUE)
|
||||
response.setContentLength((int)count);
|
||||
else
|
||||
response.setHeader("Content-Length", Long.toString(count));
|
||||
}
|
||||
|
||||
long ct = content.getCacheTime();
|
||||
if (ct>=0)
|
||||
response.setHeader("Cache-Control", "public, max-age=" + ct);
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
/* I2P additions below here */
|
||||
|
||||
/** from Jetty HttpContent.java */
|
||||
public interface HttpContent
|
||||
{
|
||||
String getContentType();
|
||||
long getLastModified();
|
||||
/** in seconds */
|
||||
int getCacheTime();
|
||||
long getContentLength();
|
||||
InputStream getInputStream() throws IOException;
|
||||
}
|
||||
|
||||
private class FileContent implements HttpContent
|
||||
{
|
||||
private final File _file;
|
||||
|
||||
public FileContent(File file)
|
||||
{
|
||||
_file = file;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public String getContentType()
|
||||
{
|
||||
//return _mimeTypes.getMimeByExtension(_file.toString());
|
||||
return getMimeType(_file.toString());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public long getLastModified()
|
||||
{
|
||||
return _file.lastModified();
|
||||
}
|
||||
|
||||
public int getCacheTime()
|
||||
{
|
||||
return FILE_CACHE_CONTROL_SECS;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public long getContentLength()
|
||||
{
|
||||
return _file.length();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public InputStream getInputStream() throws IOException
|
||||
{
|
||||
return new BufferedInputStream(new FileInputStream(_file));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return "File \"" + _file + '"'; }
|
||||
}
|
||||
|
||||
private class JarContent implements HttpContent
|
||||
{
|
||||
private final String _path;
|
||||
|
||||
public JarContent(String path)
|
||||
{
|
||||
_path = path;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public String getContentType()
|
||||
{
|
||||
return getMimeType(_path);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public long getLastModified()
|
||||
{
|
||||
String cpath = getServletContext().getContextPath();
|
||||
// this won't work if we aren't at top level
|
||||
String cname = cpath == "" ? "i2psnark" : cpath.substring(1).replace("/", "_");
|
||||
return (new File(_context.getBaseDir(), "webapps/" + cname + ".war")).lastModified();
|
||||
}
|
||||
|
||||
public int getCacheTime()
|
||||
{
|
||||
return WAR_CACHE_CONTROL_SECS;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public long getContentLength()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public InputStream getInputStream() throws IOException
|
||||
{
|
||||
InputStream rv = getServletContext().getResourceAsStream(_path);
|
||||
if (rv == null)
|
||||
throw new IOException("Not found");
|
||||
return rv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return "Jar resource \"" + _path + '"'; }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param resourcePath in the classpath, without ".properties" extension
|
||||
*/
|
||||
protected void loadMimeMap(String resourcePath) {
|
||||
_mimeTypes.loadMimeMap(resourcePath);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the MIME type by filename extension.
|
||||
* @param filename A file name
|
||||
* @return MIME type matching the longest dot extension of the
|
||||
* file name.
|
||||
*/
|
||||
protected String getMimeType(String filename) {
|
||||
String rv = _mimeTypes.getMimeByExtension(filename);
|
||||
if (rv != null)
|
||||
return rv;
|
||||
return getServletContext().getMimeType(filename);
|
||||
}
|
||||
|
||||
protected void addMimeMapping(String extension, String type) {
|
||||
_mimeTypes.addMimeMapping(extension, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple version of URIUtil.addPaths()
|
||||
* @param path may be null
|
||||
*/
|
||||
protected static String addPaths(String base, String path) {
|
||||
if (path == null)
|
||||
return base;
|
||||
return (new File(base, path)).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple version of URIUtil.decodePath()
|
||||
*/
|
||||
protected static String decodePath(String path) throws MalformedURLException {
|
||||
if (!path.contains("%"))
|
||||
return path;
|
||||
try {
|
||||
URI uri = new URI(path);
|
||||
return uri.getPath();
|
||||
} catch (URISyntaxException use) {
|
||||
// for ease of use, since a USE is not an IOE but a MUE is...
|
||||
throw new MalformedURLException(use.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple version of URIUtil.encodePath()
|
||||
*/
|
||||
protected static String encodePath(String path) throws MalformedURLException {
|
||||
try {
|
||||
URI uri = new URI(null, null, path, null);
|
||||
return uri.toString();
|
||||
} catch (URISyntaxException use) {
|
||||
// for ease of use, since a USE is not an IOE but a MUE is...
|
||||
throw new MalformedURLException(use.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write from in to out
|
||||
*/
|
||||
private void copy(InputStream in, OutputStream out) throws IOException {
|
||||
ByteArray ba = _cache.acquire();
|
||||
byte[] buf = ba.getData();
|
||||
try {
|
||||
int read = 0;
|
||||
while ( (read = in.read(buf)) != -1) {
|
||||
out.write(buf, 0, read);
|
||||
}
|
||||
} finally {
|
||||
_cache.release(ba, false);
|
||||
if (in != null)
|
||||
try { in.close(); } catch (IOException ioe) {}
|
||||
if (out != null)
|
||||
try { out.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import java.util.TreeSet;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@@ -44,42 +45,48 @@ import org.klomp.snark.Tracker;
|
||||
import org.klomp.snark.TrackerClient;
|
||||
import org.klomp.snark.dht.DHT;
|
||||
|
||||
import org.mortbay.jetty.servlet.DefaultServlet;
|
||||
import org.mortbay.resource.Resource;
|
||||
import org.mortbay.util.URIUtil;
|
||||
|
||||
/**
|
||||
* We extend Default instead of HTTPServlet so we can handle
|
||||
* i2psnark/ file requests with http:// instead of the flaky and
|
||||
* often-blocked-by-the-browser file://
|
||||
* Refactored to eliminate Jetty dependencies.
|
||||
*/
|
||||
public class I2PSnarkServlet extends DefaultServlet {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
public class I2PSnarkServlet extends BasicServlet {
|
||||
/** generally "/i2psnark" */
|
||||
private String _contextPath;
|
||||
/** generally "i2psnark" */
|
||||
private String _contextName;
|
||||
private SnarkManager _manager;
|
||||
private static long _nonce;
|
||||
private Resource _resourceBase;
|
||||
private String _themePath;
|
||||
private String _imgPath;
|
||||
private String _lastAnnounceURL;
|
||||
|
||||
private static final String DEFAULT_NAME = "i2psnark";
|
||||
public static final String PROP_CONFIG_FILE = "i2psnark.configFile";
|
||||
|
||||
public I2PSnarkServlet() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig cfg) throws ServletException {
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_log = _context.logManager().getLog(I2PSnarkServlet.class);
|
||||
super.init(cfg);
|
||||
String cpath = getServletContext().getContextPath();
|
||||
_contextPath = cpath == "" ? "/" : cpath;
|
||||
_contextName = cpath == "" ? DEFAULT_NAME : cpath.substring(1).replace("/", "_");
|
||||
_nonce = _context.random().nextLong();
|
||||
_manager = new SnarkManager(_context);
|
||||
// limited protection against overwriting other config files or directories
|
||||
// in case you named your war "router.war"
|
||||
String configName = _contextName;
|
||||
if (!configName.equals(DEFAULT_NAME))
|
||||
configName = DEFAULT_NAME + '_' + _contextName;
|
||||
_manager = new SnarkManager(_context, _contextPath, configName);
|
||||
String configFile = _context.getProperty(PROP_CONFIG_FILE);
|
||||
if ( (configFile == null) || (configFile.trim().length() <= 0) )
|
||||
configFile = "i2psnark.config";
|
||||
configFile = configName + ".config";
|
||||
_manager.loadConfig(configFile);
|
||||
_manager.start();
|
||||
try {
|
||||
_resourceBase = Resource.newResource(_manager.getDataDir().getAbsolutePath());
|
||||
} catch (IOException ioe) {}
|
||||
super.init(cfg);
|
||||
loadMimeMap("org/klomp/snark/web/mime");
|
||||
setResourceBase(_manager.getDataDir());
|
||||
setWarBase("/.icons/");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,29 +102,36 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
* and we can't get any resources (like icons) out of the .war
|
||||
*/
|
||||
@Override
|
||||
public Resource getResource(String pathInContext)
|
||||
public File getResource(String pathInContext)
|
||||
{
|
||||
if (pathInContext == null || pathInContext.equals("/") || pathInContext.equals("/index.jsp") ||
|
||||
pathInContext.equals("/index.html") || pathInContext.startsWith("/.icons/"))
|
||||
return super.getResource(pathInContext);
|
||||
// files in the i2psnark/ directory
|
||||
try {
|
||||
return _resourceBase.addPath(pathInContext);
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
return new File(_resourceBase, pathInContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the browser to cache the icons
|
||||
* Handle what we can here, calling super.doGet() for the rest.
|
||||
* @since 0.8.3
|
||||
*/
|
||||
@Override
|
||||
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
super.doGet(request, response);
|
||||
doGetAndPost(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle what we can here, calling super.doPost() for the rest.
|
||||
* @since Jetty 7
|
||||
*/
|
||||
@Override
|
||||
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
doGetAndPost(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle what we can here, calling super.doGet() or super.doPost() for the rest.
|
||||
*
|
||||
* Some parts modified from:
|
||||
* <pre>
|
||||
// ========================================================================
|
||||
@@ -137,14 +151,11 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
private void doGetAndPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Service " + req.getMethod() + " \"" + req.getContextPath() + "\" \"" + req.getServletPath() + "\" \"" + req.getPathInfo() + '"');
|
||||
// since we are not overriding handle*(), do this here
|
||||
String method = req.getMethod();
|
||||
if (!(method.equals("GET") || method.equals("HEAD") || method.equals("POST"))) {
|
||||
resp.sendError(405);
|
||||
return;
|
||||
}
|
||||
_themePath = "/themes/snark/" + _manager.getTheme() + '/';
|
||||
_imgPath = _themePath + "images/";
|
||||
// this is the part after /i2psnark
|
||||
@@ -152,6 +163,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
resp.setHeader("X-Frame-Options", "SAMEORIGIN");
|
||||
|
||||
String peerParam = req.getParameter("p");
|
||||
String stParam = req.getParameter("st");
|
||||
String peerString;
|
||||
if (peerParam == null || (!_manager.util().connected()) ||
|
||||
peerParam.replaceAll("[a-zA-Z0-9~=-]", "").length() > 0) { // XSS
|
||||
@@ -159,6 +171,12 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
} else {
|
||||
peerString = "?p=" + peerParam;
|
||||
}
|
||||
if (stParam != null && !stParam.equals("0")) {
|
||||
if (peerString.length() > 0)
|
||||
peerString += "&st=" + stParam;
|
||||
else
|
||||
peerString = "?st="+ stParam;
|
||||
}
|
||||
|
||||
// AJAX for mainsection
|
||||
if ("/.ajax/xhr1.html".equals(path)) {
|
||||
@@ -176,17 +194,18 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
// 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.endsWith("/")) {
|
||||
// Listing of a torrent (torrent detail page)
|
||||
// bypass the horrid Resource.getListHTML()
|
||||
String pathInfo = req.getPathInfo();
|
||||
String pathInContext = URIUtil.addPaths(path, pathInfo);
|
||||
String pathInContext = addPaths(path, pathInfo);
|
||||
req.setCharacterEncoding("UTF-8");
|
||||
resp.setCharacterEncoding("UTF-8");
|
||||
resp.setContentType("text/html; charset=UTF-8");
|
||||
Resource resource = getResource(pathInContext);
|
||||
if (resource == null || (!resource.exists())) {
|
||||
File resource = getResource(pathInContext);
|
||||
if (resource == null) {
|
||||
resp.sendError(404);
|
||||
} else {
|
||||
String base = URIUtil.addPaths(req.getRequestURI(), "/");
|
||||
String base = addPaths(req.getRequestURI(), "/");
|
||||
String listing = getListHTML(resource, base, true, method.equals("POST") ? req.getParameterMap() : null);
|
||||
if (method.equals("POST")) {
|
||||
// P-R-G
|
||||
@@ -198,11 +217,19 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
super.service(req, resp);
|
||||
// local completed files in torrent directories
|
||||
if (method.equals("GET") || method.equals("HEAD"))
|
||||
super.doGet(req, resp);
|
||||
else if (method.equals("POST"))
|
||||
super.doPost(req, resp);
|
||||
else
|
||||
resp.sendError(405);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Either the main page or /configure
|
||||
|
||||
req.setCharacterEncoding("UTF-8");
|
||||
resp.setCharacterEncoding("UTF-8");
|
||||
resp.setContentType("text/html; charset=UTF-8");
|
||||
@@ -236,7 +263,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
out.write("<script src=\"/js/ajax.js\" type=\"text/javascript\"></script>\n" +
|
||||
"<script type=\"text/javascript\">\n" +
|
||||
"var failMessage = \"<div class=\\\"routerdown\\\"><b>" + _("Router is down") + "<\\/b><\\/div>\";\n" +
|
||||
"function requestAjax1() { ajax(\"/i2psnark/.ajax/xhr1.html" + peerString + "\", \"mainsection\", " + (delay*1000) + "); }\n" +
|
||||
"function requestAjax1() { ajax(\"" + _contextPath + "/.ajax/xhr1.html" + peerString + "\", \"mainsection\", " + (delay*1000) + "); }\n" +
|
||||
"function initAjax() { setTimeout(requestAjax1, " + (delay*1000) +"); }\n" +
|
||||
"</script>\n");
|
||||
}
|
||||
@@ -249,18 +276,24 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
out.write("<center>");
|
||||
List<Tracker> sortedTrackers = null;
|
||||
if (isConfigure) {
|
||||
out.write("<div class=\"snarknavbar\"><a href=\"/i2psnark/\" title=\"");
|
||||
out.write("<div class=\"snarknavbar\"><a href=\"" + _contextPath + "/\" title=\"");
|
||||
out.write(_("Torrents"));
|
||||
out.write("\" class=\"snarkRefresh\">");
|
||||
out.write("<img alt=\"\" border=\"0\" src=\"" + _imgPath + "arrow_refresh.png\"> ");
|
||||
out.write(_("I2PSnark"));
|
||||
if (_contextName.equals(DEFAULT_NAME))
|
||||
out.write(_("I2PSnark"));
|
||||
else
|
||||
out.write(_contextName);
|
||||
out.write("</a>");
|
||||
} else {
|
||||
out.write("<div class=\"snarknavbar\"><a href=\"/i2psnark/" + peerString + "\" title=\"");
|
||||
out.write("<div class=\"snarknavbar\"><a href=\"" + _contextPath + '/' + peerString + "\" title=\"");
|
||||
out.write(_("Refresh page"));
|
||||
out.write("\" class=\"snarkRefresh\">");
|
||||
out.write("<img alt=\"\" border=\"0\" src=\"" + _imgPath + "arrow_refresh.png\"> ");
|
||||
out.write(_("I2PSnark"));
|
||||
if (_contextName.equals(DEFAULT_NAME))
|
||||
out.write(_("I2PSnark"));
|
||||
else
|
||||
out.write(_contextName);
|
||||
out.write("</a> <a href=\"http://forum.i2p/viewforum.php?f=21\" class=\"snarkRefresh\" target=\"_blank\">");
|
||||
out.write(_("Forum"));
|
||||
out.write("</a>\n");
|
||||
@@ -302,7 +335,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
List<String> msgs = _manager.getMessages();
|
||||
if (!msgs.isEmpty()) {
|
||||
out.write("<div class=\"snarkMessages\">");
|
||||
out.write("<a href=\"/i2psnark/");
|
||||
out.write("<a href=\"" + _contextPath + '/');
|
||||
if (isConfigure)
|
||||
out.write("configure");
|
||||
if (peerString.length() > 0)
|
||||
@@ -325,6 +358,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
/** dl, ul, down rate, up rate, peers, size */
|
||||
final long stats[] = {0,0,0,0,0,0};
|
||||
String peerParam = req.getParameter("p");
|
||||
String stParam = req.getParameter("st");
|
||||
|
||||
List<Snark> snarks = getSortedSnarks(req);
|
||||
boolean isForm = _manager.util().connected() || !snarks.isEmpty();
|
||||
@@ -334,6 +368,9 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
// don't lose peer setting
|
||||
if (peerParam != null)
|
||||
out.write("<input type=\"hidden\" name=\"p\" value=\"" + peerParam + "\" >\n");
|
||||
// ...or st setting
|
||||
if (stParam != null)
|
||||
out.write("<input type=\"hidden\" name=\"st\" value=\"" + stParam + "\" >\n");
|
||||
}
|
||||
out.write(TABLE_HEADER);
|
||||
out.write("<img border=\"0\" src=\"" + _imgPath + "status.png\" title=\"");
|
||||
@@ -342,8 +379,12 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
out.write(_("Status"));
|
||||
out.write("\"></th>\n<th>");
|
||||
if (_manager.util().connected() && !snarks.isEmpty()) {
|
||||
out.write(" <a href=\"/i2psnark/");
|
||||
out.write(" <a href=\"" + _contextPath + '/');
|
||||
if (peerParam != null) {
|
||||
if (stParam != null) {
|
||||
out.write("?st=");
|
||||
out.write(stParam);
|
||||
}
|
||||
out.write("\">");
|
||||
out.write("<img border=\"0\" src=\"" + _imgPath + "hidepeers.png\" title=\"");
|
||||
out.write(_("Hide Peers"));
|
||||
@@ -351,7 +392,12 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
out.write(_("Hide Peers"));
|
||||
out.write("\">");
|
||||
} else {
|
||||
out.write("?p=1\">");
|
||||
out.write("?p=1");
|
||||
if (stParam != null) {
|
||||
out.write("&st=");
|
||||
out.write(stParam);
|
||||
}
|
||||
out.write("\">");
|
||||
out.write("<img border=\"0\" src=\"" + _imgPath + "showpeers.png\" title=\"");
|
||||
out.write(_("Show Peers"));
|
||||
out.write("\" alt=\"");
|
||||
@@ -423,7 +469,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
out.write(" ");
|
||||
} else if (_manager.util().connected()) {
|
||||
if (isDegraded)
|
||||
out.write("<a href=\"/i2psnark/?action=StopAll&nonce=" + _nonce + "\"><img title=\"");
|
||||
out.write("<a href=\"" + _contextPath + "/?action=StopAll&nonce=" + _nonce + "\"><img title=\"");
|
||||
else {
|
||||
// http://www.onenaught.com/posts/382/firefox-4-change-input-type-image-only-submits-x-and-y-not-name
|
||||
//out.write("<input type=\"image\" name=\"action\" value=\"StopAll\" title=\"");
|
||||
@@ -437,7 +483,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
out.write("</a>");
|
||||
} else if ((!_manager.util().isConnecting()) && !snarks.isEmpty()) {
|
||||
if (isDegraded)
|
||||
out.write("<a href=\"/i2psnark/?action=StartAll&nonce=" + _nonce + "\"><img title=\"");
|
||||
out.write("<a href=\"/" + _contextPath + "/?action=StartAll&nonce=" + _nonce + "\"><img title=\"");
|
||||
else
|
||||
out.write("<input type=\"image\" name=\"action_StartAll\" value=\"foo\" title=\"");
|
||||
out.write(_("Start all torrents and the I2P tunnel"));
|
||||
@@ -450,12 +496,21 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
out.write(" ");
|
||||
}
|
||||
out.write("</th></tr></thead>\n");
|
||||
String uri = "/i2psnark/";
|
||||
String uri = _contextPath + '/';
|
||||
boolean showDebug = "2".equals(peerParam);
|
||||
|
||||
int start = 0;
|
||||
if (stParam != null) {
|
||||
try {
|
||||
start = Math.max(0, Math.min(snarks.size() - 1, Integer.parseInt(stParam)));
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
int pageSize = _manager.getPageSize();
|
||||
for (int i = 0; i < snarks.size(); i++) {
|
||||
Snark snark = (Snark)snarks.get(i);
|
||||
boolean showPeers = showDebug || "1".equals(peerParam) || Base64.encode(snark.getInfoHash()).equals(peerParam);
|
||||
displaySnark(out, snark, uri, i, stats, showPeers, isDegraded, noThinsp, showDebug);
|
||||
boolean hide = i < start || i >= start + pageSize;
|
||||
displaySnark(out, snark, uri, i, stats, showPeers, isDegraded, noThinsp, showDebug, hide);
|
||||
}
|
||||
|
||||
if (snarks.isEmpty()) {
|
||||
@@ -467,6 +522,27 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
} else /** if (snarks.size() > 1) */ {
|
||||
out.write("<tfoot><tr>\n" +
|
||||
" <th align=\"left\" colspan=\"6\">");
|
||||
if (start > 0) {
|
||||
int prev = Math.max(0, start - pageSize);
|
||||
out.write(" <a href=\"" + _contextPath + "?st=" + prev);
|
||||
if (peerParam != null)
|
||||
out.write("&p=" + peerParam);
|
||||
out.write("\">" +
|
||||
"<img alt=\"" + _("Prev") + "\" title=\"" + _("Previous page") + "\" border=\"0\" src=\"" +
|
||||
_imgPath + "control_rewind_blue.png\">" +
|
||||
"</a> ");
|
||||
}
|
||||
if (start + pageSize < snarks.size()) {
|
||||
int next = start + pageSize;
|
||||
out.write(" <a href=\"" + _contextPath + "?st=" + next);
|
||||
if (peerParam != null)
|
||||
out.write("&p=" + peerParam);
|
||||
out.write("\">" +
|
||||
"<img alt=\"" + _("Next") + "\" title=\"" + _("Next page") + "\" border=\"0\" src=\"" +
|
||||
_imgPath + "control_fastforward_blue.png\">" +
|
||||
"</a> ");
|
||||
}
|
||||
out.write(" ");
|
||||
out.write(_("Totals"));
|
||||
out.write(": ");
|
||||
out.write(ngettext("1 torrent", "{0} torrents", snarks.size()));
|
||||
@@ -716,13 +792,18 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
String upBW = req.getParameter("upBW");
|
||||
String refreshDel = req.getParameter("refreshDelay");
|
||||
String startupDel = req.getParameter("startupDelay");
|
||||
String pageSize = req.getParameter("pageSize");
|
||||
boolean useOpenTrackers = req.getParameter("useOpenTrackers") != null;
|
||||
boolean useDHT = req.getParameter("useDHT") != null;
|
||||
//String openTrackers = req.getParameter("openTrackers");
|
||||
String theme = req.getParameter("theme");
|
||||
_manager.updateConfig(dataDir, filesPublic, autoStart, refreshDel, startupDel,
|
||||
_manager.updateConfig(dataDir, filesPublic, autoStart, refreshDel, startupDel, pageSize,
|
||||
seedPct, eepHost, eepPort, i2cpHost, i2cpPort, i2cpOpts,
|
||||
upLimit, upBW, useOpenTrackers, useDHT, theme);
|
||||
// update servlet
|
||||
try {
|
||||
setResourceBase(_manager.getDataDir());
|
||||
} catch (ServletException se) {}
|
||||
} else if ("Save2".equals(action)) {
|
||||
String taction = req.getParameter("taction");
|
||||
if (taction != null)
|
||||
@@ -825,7 +906,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
url = url.substring(0, url.length() - 5);
|
||||
buf.append(url);
|
||||
if (p.length() > 0)
|
||||
buf.append('?').append(p);
|
||||
buf.append(p);
|
||||
resp.setHeader("Location", buf.toString());
|
||||
resp.sendError(302, "Moved");
|
||||
}
|
||||
@@ -989,8 +1070,34 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
|
||||
private static final int MAX_DISPLAYED_FILENAME_LENGTH = 50;
|
||||
private static final int MAX_DISPLAYED_ERROR_LENGTH = 43;
|
||||
|
||||
/**
|
||||
* Display one snark (one line in table, unless showPeers is true)
|
||||
*
|
||||
* @param stats in/out param (totals)
|
||||
* @param statsOnly if true, output nothing, update stats only
|
||||
*/
|
||||
private void displaySnark(PrintWriter out, Snark snark, String uri, int row, long stats[], boolean showPeers,
|
||||
boolean isDegraded, boolean noThinsp, boolean showDebug) throws IOException {
|
||||
boolean isDegraded, boolean noThinsp, boolean showDebug, boolean statsOnly) throws IOException {
|
||||
// stats
|
||||
long uploaded = snark.getUploaded();
|
||||
stats[0] += snark.getDownloaded();
|
||||
stats[1] += uploaded;
|
||||
long downBps = snark.getDownloadRate();
|
||||
long upBps = snark.getUploadRate();
|
||||
boolean isRunning = !snark.isStopped();
|
||||
if (isRunning) {
|
||||
stats[2] += downBps;
|
||||
stats[3] += upBps;
|
||||
}
|
||||
int curPeers = snark.getPeerCount();
|
||||
stats[4] += curPeers;
|
||||
long total = snark.getTotalLength();
|
||||
if (total > 0)
|
||||
stats[5] += total;
|
||||
if (statsOnly)
|
||||
return;
|
||||
|
||||
String filename = snark.getName();
|
||||
if (snark.getMetaInfo() != null) {
|
||||
// Only do this if not a magnet or torrent download
|
||||
@@ -1010,7 +1117,6 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
filename = start + "…";
|
||||
}
|
||||
}
|
||||
long total = snark.getTotalLength();
|
||||
// includes skipped files, -1 for magnet mode
|
||||
long remaining = snark.getRemainingLength();
|
||||
if (remaining > total)
|
||||
@@ -1019,22 +1125,11 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
long needed = snark.getNeededLength();
|
||||
if (needed > total)
|
||||
needed = total;
|
||||
long downBps = snark.getDownloadRate();
|
||||
long upBps = snark.getUploadRate();
|
||||
long remainingSeconds;
|
||||
if (downBps > 0 && needed > 0)
|
||||
remainingSeconds = needed / downBps;
|
||||
else
|
||||
remainingSeconds = -1;
|
||||
boolean isRunning = !snark.isStopped();
|
||||
long uploaded = snark.getUploaded();
|
||||
stats[0] += snark.getDownloaded();
|
||||
stats[1] += uploaded;
|
||||
if (isRunning) {
|
||||
stats[2] += downBps;
|
||||
stats[3] += upBps;
|
||||
}
|
||||
stats[5] += total;
|
||||
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
// isValid means isNotMagnet
|
||||
@@ -1042,8 +1137,6 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
boolean isMultiFile = isValid && meta.getFiles() != null;
|
||||
|
||||
String err = snark.getTrackerProblems();
|
||||
int curPeers = snark.getPeerCount();
|
||||
stats[4] += curPeers;
|
||||
int knownPeers = Math.max(curPeers, snark.getTrackerSeenPeers());
|
||||
|
||||
String rowClass = (row % 2 == 0 ? "snarkTorrentEven" : "snarkTorrentOdd");
|
||||
@@ -1245,7 +1338,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
} else if (isRunning) {
|
||||
// Stop Button
|
||||
if (isDegraded)
|
||||
out.write("<a href=\"/i2psnark/?action=Stop_" + b64 + "&nonce=" + _nonce + "\"><img title=\"");
|
||||
out.write("<a href=\"" + _contextPath + "/?action=Stop_" + b64 + "&nonce=" + _nonce + "\"><img title=\"");
|
||||
else
|
||||
out.write("<input type=\"image\" name=\"action_Stop_" + b64 + "\" value=\"foo\" title=\"");
|
||||
out.write(_("Stop the torrent"));
|
||||
@@ -1259,7 +1352,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
// Start Button
|
||||
// This works in Opera but it's displayed a little differently, so use noThinsp here too so all 3 icons are consistent
|
||||
if (noThinsp)
|
||||
out.write("<a href=\"/i2psnark/?action=Start_" + b64 + "&nonce=" + _nonce + "\"><img title=\"");
|
||||
out.write("<a href=\"/" + _contextPath + "/?action=Start_" + b64 + "&nonce=" + _nonce + "\"><img title=\"");
|
||||
else
|
||||
out.write("<input type=\"image\" name=\"action_Start_" + b64 + "\" value=\"foo\" title=\"");
|
||||
out.write(_("Start the torrent"));
|
||||
@@ -1273,7 +1366,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
// Remove Button
|
||||
// Doesnt work with Opera so use noThinsp instead of isDegraded
|
||||
if (noThinsp)
|
||||
out.write("<a href=\"/i2psnark/?action=Remove_" + b64 + "&nonce=" + _nonce + "\"><img title=\"");
|
||||
out.write("<a href=\"" + _contextPath + "/?action=Remove_" + b64 + "&nonce=" + _nonce + "\"><img title=\"");
|
||||
else
|
||||
out.write("<input type=\"image\" name=\"action_Remove_" + b64 + "\" value=\"foo\" title=\"");
|
||||
out.write(_("Remove the torrent from the active list, deleting the .torrent file"));
|
||||
@@ -1293,7 +1386,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
// Delete Button
|
||||
// Doesnt work with Opera so use noThinsp instead of isDegraded
|
||||
if (noThinsp)
|
||||
out.write("<a href=\"/i2psnark/?action=Delete_" + b64 + "&nonce=" + _nonce + "\"><img title=\"");
|
||||
out.write("<a href=\"" + _contextPath + "/?action=Delete_" + b64 + "&nonce=" + _nonce + "\"><img title=\"");
|
||||
else
|
||||
out.write("<input type=\"image\" name=\"action_Delete_" + b64 + "\" value=\"foo\" title=\"");
|
||||
out.write(_("Delete the .torrent file and the associated data file(s)"));
|
||||
@@ -1524,7 +1617,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
out.write(_("Add Torrent"));
|
||||
out.write("</span><hr>\n<table border=\"0\"><tr><td>");
|
||||
out.write(_("From URL"));
|
||||
out.write(":<td><input type=\"text\" name=\"newURL\" size=\"85\" value=\"" + newURL + "\"");
|
||||
out.write(":<td><input type=\"text\" name=\"newURL\" size=\"85\" value=\"" + newURL + "\" spellcheck=\"false\"");
|
||||
out.write(" title=\"");
|
||||
out.write(_("Enter the torrent file download URL (I2P only), magnet link, maggot link, or info hash"));
|
||||
out.write("\"> \n");
|
||||
@@ -1565,7 +1658,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
out.write(_("Data to seed"));
|
||||
out.write(":<td><code>" + _manager.getDataDir().getAbsolutePath() + File.separatorChar
|
||||
+ "</code><input type=\"text\" name=\"baseFile\" size=\"58\" value=\"" + baseFile
|
||||
+ "\" title=\"");
|
||||
+ "\" spellcheck=\"false\" title=\"");
|
||||
out.write(_("File or directory to seed (must be within the specified path)"));
|
||||
out.write("\" ><tr><td>\n");
|
||||
out.write(_("Trackers"));
|
||||
@@ -1620,7 +1713,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
boolean useDHT = _manager.util().shouldUseDHT();
|
||||
//int seedPct = 0;
|
||||
|
||||
out.write("<form action=\"/i2psnark/configure\" method=\"POST\">\n" +
|
||||
out.write("<form action=\"" + _contextPath + "/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=\"Save\" >\n" +
|
||||
@@ -1631,9 +1724,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
"<table border=\"0\"><tr><td>");
|
||||
|
||||
out.write(_("Data directory"));
|
||||
out.write(": <td><code>" + dataDir + "</code> <i>(");
|
||||
out.write(_("Edit i2psnark.config and restart to change"));
|
||||
out.write(")</i><br>\n" +
|
||||
out.write(": <td><input name=\"dataDir\" size=\"80\" value=\"" + dataDir + "\" spellcheck=\"false\"></td>\n" +
|
||||
|
||||
"<tr><td>");
|
||||
out.write(_("Files readable by all"));
|
||||
@@ -1685,8 +1776,14 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
|
||||
"<tr><td>");
|
||||
out.write(_("Startup delay"));
|
||||
out.write(": <td><input name=\"startupDelay\" size=\"3\" class=\"r\" value=\"" + _manager.util().getStartupDelay() + "\"> ");
|
||||
out.write(": <td><input name=\"startupDelay\" size=\"4\" class=\"r\" value=\"" + _manager.util().getStartupDelay() + "\"> ");
|
||||
out.write(_("minutes"));
|
||||
out.write("<br>\n" +
|
||||
|
||||
"<tr><td>");
|
||||
out.write(_("Page size"));
|
||||
out.write(": <td><input name=\"pageSize\" size=\"4\" maxlength=\"6\" class=\"r\" value=\"" + _manager.getPageSize() + "\"> ");
|
||||
out.write(_("torrents"));
|
||||
out.write("<br>\n");
|
||||
|
||||
|
||||
@@ -1712,7 +1809,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
out.write("<tr><td>");
|
||||
out.write(_("Total uploader limit"));
|
||||
out.write(": <td><input type=\"text\" name=\"upLimit\" class=\"r\" value=\""
|
||||
+ _manager.util().getMaxUploaders() + "\" size=\"3\" maxlength=\"3\" > ");
|
||||
+ _manager.util().getMaxUploaders() + "\" size=\"4\" maxlength=\"3\" > ");
|
||||
out.write(_("peers"));
|
||||
out.write("<br>\n" +
|
||||
|
||||
@@ -1802,7 +1899,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
/** @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" +
|
||||
buf.append("<form action=\"" + _contextPath + "/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" +
|
||||
@@ -1856,11 +1953,11 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
}
|
||||
buf.append("<tr><td><b>")
|
||||
.append(_("Add")).append(":</b></td>" +
|
||||
"<td><input type=\"text\" class=\"trackername\" name=\"tname\"></td>" +
|
||||
"<td><input type=\"text\" class=\"trackerhome\" name=\"thurl\"></td>" +
|
||||
"<td><input type=\"text\" class=\"trackername\" name=\"tname\" spellcheck=\"false\"></td>" +
|
||||
"<td><input type=\"text\" class=\"trackerhome\" name=\"thurl\" spellcheck=\"false\"></td>" +
|
||||
"<td><input type=\"checkbox\" class=\"optbox\" name=\"_add_open_\"></td>" +
|
||||
"<td><input type=\"checkbox\" class=\"optbox\" name=\"_add_private_\"></td>" +
|
||||
"<td><input type=\"text\" class=\"trackerannounce\" name=\"taurl\"></td></tr>\n" +
|
||||
"<td><input type=\"text\" class=\"trackerannounce\" name=\"taurl\" spellcheck=\"false\"></td></tr>\n" +
|
||||
"<tr><td colspan=\"6\"> </td></tr>\n" + // spacer
|
||||
"<tr><td colspan=\"2\"></td><td colspan=\"4\">\n" +
|
||||
"<input type=\"submit\" name=\"taction\" class=\"default\" value=\"").append(_("Add tracker")).append("\">\n" +
|
||||
@@ -2031,7 +2128,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
* @return String of HTML or null if postParams != null
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private String getListHTML(Resource r, String base, boolean parent, Map postParams)
|
||||
private String getListHTML(File r, String base, boolean parent, Map postParams)
|
||||
throws IOException
|
||||
{
|
||||
String[] ls = null;
|
||||
@@ -2040,9 +2137,10 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
Arrays.sort(ls, Collator.getInstance());
|
||||
} // if r is not a directory, we are only showing torrent info section
|
||||
|
||||
String title = URIUtil.decodePath(base);
|
||||
if (title.startsWith("/i2psnark/"))
|
||||
title = title.substring("/i2psnark/".length());
|
||||
String title = decodePath(base);
|
||||
String cpath = _contextPath + '/';
|
||||
if (title.startsWith(cpath))
|
||||
title = title.substring(cpath.length());
|
||||
|
||||
// Get the snark associated with this directory
|
||||
String torrentName;
|
||||
@@ -2067,8 +2165,13 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
title = _("Torrent") + ": " + title;
|
||||
buf.append(title);
|
||||
buf.append("</TITLE>").append(HEADER_A).append(_themePath).append(HEADER_B).append("<link rel=\"shortcut icon\" href=\"" + _themePath + "favicon.ico\">" +
|
||||
"</HEAD><BODY>\n<center><div class=\"snarknavbar\"><a href=\"/i2psnark/\" title=\"Torrents\"");
|
||||
buf.append(" class=\"snarkRefresh\"><img alt=\"\" border=\"0\" src=\"" + _imgPath + "arrow_refresh.png\"> I2PSnark</a></div></center>\n");
|
||||
"</HEAD><BODY>\n<center><div class=\"snarknavbar\"><a href=\"").append(_contextPath).append("/\" title=\"Torrents\"");
|
||||
buf.append(" class=\"snarkRefresh\"><img alt=\"\" border=\"0\" src=\"" + _imgPath + "arrow_refresh.png\"> ");
|
||||
if (_contextName.equals(DEFAULT_NAME))
|
||||
buf.append(_("I2PSnark"));
|
||||
else
|
||||
buf.append(_contextName);
|
||||
buf.append("</a></div></center>\n");
|
||||
|
||||
if (parent) // always true
|
||||
buf.append("<div class=\"page\"><div class=\"mainsection\">");
|
||||
@@ -2089,7 +2192,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
buf.append("<tr><td>")
|
||||
.append("<img alt=\"\" border=\"0\" src=\"" + _imgPath + "file.png\" > <b>")
|
||||
.append(_("Torrent file"))
|
||||
.append(":</b> <a href=\"/i2psnark/").append(baseName).append("\">")
|
||||
.append(":</b> <a href=\"").append(_contextPath).append('/').append(baseName).append("\">")
|
||||
.append(fullPath)
|
||||
.append("</a></td></tr>\n");
|
||||
|
||||
@@ -2228,7 +2331,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
.append("\"></th>\n");
|
||||
buf.append("</tr>\n</thead>\n");
|
||||
buf.append("<tr><td colspan=\"" + (showPriority ? '5' : '4') + "\" class=\"ParentDir\"><A HREF=\"");
|
||||
buf.append(URIUtil.addPaths(base,"../"));
|
||||
buf.append(addPaths(base,"../"));
|
||||
buf.append("\"><img alt=\"\" border=\"0\" src=\"" + _imgPath + "up.png\"> ")
|
||||
.append(_("Up to higher level directory"))
|
||||
.append("</A></td></tr>\n");
|
||||
@@ -2239,12 +2342,12 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
boolean showSaveButton = false;
|
||||
for (int i=0 ; i< ls.length ; i++)
|
||||
{
|
||||
String encoded=URIUtil.encodePath(ls[i]);
|
||||
String encoded = encodePath(ls[i]);
|
||||
// bugfix for I2P - Backport from Jetty 6 (zero file lengths and last-modified times)
|
||||
// http://jira.codehaus.org/browse/JETTY-361?page=com.atlassian.jira.plugin.system.issuetabpanels%3Achangehistory-tabpanel#issue-tabs
|
||||
// See resource.diff attachment
|
||||
//Resource item = addPath(encoded);
|
||||
Resource item = r.addPath(ls[i]);
|
||||
File item = new File(r, ls[i]);
|
||||
|
||||
String rowClass = (i % 2 == 0 ? "snarkTorrentEven" : "snarkTorrentOdd");
|
||||
buf.append("<TR class=\"").append(rowClass).append("\">");
|
||||
@@ -2264,7 +2367,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
} else {
|
||||
Storage storage = snark.getStorage();
|
||||
try {
|
||||
File f = item.getFile();
|
||||
File f = item;
|
||||
if (f != null) {
|
||||
long remaining = storage.remaining(f.getCanonicalPath());
|
||||
if (remaining < 0) {
|
||||
@@ -2294,9 +2397,9 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
}
|
||||
}
|
||||
|
||||
String path=URIUtil.addPaths(base,encoded);
|
||||
String path=addPaths(base,encoded);
|
||||
if (item.isDirectory() && !path.endsWith("/"))
|
||||
path=URIUtil.addPaths(path,"/");
|
||||
path=addPaths(path,"/");
|
||||
String icon = toIcon(item);
|
||||
|
||||
buf.append("<TD class=\"snarkFileIcon ")
|
||||
@@ -2331,7 +2434,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
buf.append("</TD>");
|
||||
if (showPriority) {
|
||||
buf.append("<td class=\"priority\">");
|
||||
File f = item.getFile();
|
||||
File f = item;
|
||||
if ((!complete) && (!item.isDirectory()) && f != null) {
|
||||
int pri = snark.getStorage().getPriority(f.getCanonicalPath());
|
||||
buf.append("<input type=\"radio\" value=\"5\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
|
||||
@@ -2368,7 +2471,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
}
|
||||
|
||||
/** @since 0.7.14 */
|
||||
private String toIcon(Resource item) {
|
||||
private String toIcon(File item) {
|
||||
if (item.isDirectory())
|
||||
return "folder";
|
||||
return toIcon(item.toString());
|
||||
@@ -2381,40 +2484,33 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
*/
|
||||
private String toIcon(String path) {
|
||||
String icon;
|
||||
// Should really just add to the mime.properties file in org.mortbay.jetty.jar
|
||||
// instead of this mishmash. We can't get to HttpContext.setMimeMapping()
|
||||
// from here? We could do it from a web.xml perhaps.
|
||||
// Or could we put our own org/mortbay/http/mime.properties file in the war?
|
||||
// Note that for this to work well, our custom mime.properties file must be loaded.
|
||||
String plc = path.toLowerCase(Locale.US);
|
||||
String mime = getServletContext().getMimeType(path);
|
||||
String mime = getMimeType(path);
|
||||
if (mime == null)
|
||||
mime = "";
|
||||
if (mime.equals("text/html"))
|
||||
icon = "html";
|
||||
else if (mime.equals("text/plain") || plc.endsWith(".nfo") ||
|
||||
else if (mime.equals("text/plain") ||
|
||||
mime.equals("application/rtf"))
|
||||
icon = "page";
|
||||
else if (mime.equals("application/java-archive") || plc.endsWith(".war") ||
|
||||
else if (mime.equals("application/java-archive") ||
|
||||
plc.endsWith(".deb"))
|
||||
icon = "package";
|
||||
else if (plc.endsWith(".xpi2p"))
|
||||
icon = "plugin";
|
||||
else if (mime.equals("application/pdf"))
|
||||
icon = "page_white_acrobat";
|
||||
else if (mime.startsWith("image/") || plc.endsWith(".ico"))
|
||||
else if (mime.startsWith("image/"))
|
||||
icon = "photo";
|
||||
else if (mime.startsWith("audio/") || mime.equals("application/ogg") ||
|
||||
plc.endsWith(".flac") || plc.endsWith(".m4a") || plc.endsWith(".wma") ||
|
||||
plc.endsWith(".ape") || plc.endsWith(".oga"))
|
||||
else if (mime.startsWith("audio/") || mime.equals("application/ogg"))
|
||||
icon = "music";
|
||||
else if (mime.startsWith("video/") || plc.endsWith(".mkv") || plc.endsWith(".m4v") ||
|
||||
plc.endsWith(".mp4") || plc.endsWith(".wmv") || plc.endsWith(".flv") ||
|
||||
plc.endsWith(".ogm") || plc.endsWith(".ogv"))
|
||||
else if (mime.startsWith("video/"))
|
||||
icon = "film";
|
||||
else if (mime.equals("application/zip") || mime.equals("application/x-gtar") ||
|
||||
mime.equals("application/compress") || mime.equals("application/gzip") ||
|
||||
mime.equals("application/x-tar") ||
|
||||
plc.endsWith(".rar") || plc.endsWith(".bz2") || plc.endsWith(".7z"))
|
||||
mime.equals("application/x-7z-compressed") || mime.equals("application/x-rar-compresed") ||
|
||||
mime.equals("application/x-tar") || mime.equals("application/x-bzip2"))
|
||||
icon = "compress";
|
||||
else if (plc.endsWith(".exe"))
|
||||
icon = "application";
|
||||
@@ -2426,13 +2522,13 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
}
|
||||
|
||||
/** @since 0.7.14 */
|
||||
private static String toImg(String icon) {
|
||||
return "<img alt=\"\" height=\"16\" width=\"16\" src=\"/i2psnark/.icons/" + icon + ".png\">";
|
||||
private String toImg(String icon) {
|
||||
return "<img alt=\"\" height=\"16\" width=\"16\" src=\"" + _contextPath + "/.icons/" + icon + ".png\">";
|
||||
}
|
||||
|
||||
/** @since 0.8.2 */
|
||||
private static String toImg(String icon, String altText) {
|
||||
return "<img alt=\"" + altText + "\" height=\"16\" width=\"16\" src=\"/i2psnark/.icons/" + icon + ".png\">";
|
||||
private String toImg(String icon, String altText) {
|
||||
return "<img alt=\"" + altText + "\" height=\"16\" width=\"16\" src=\"" + _contextPath + "/.icons/" + icon + ".png\">";
|
||||
}
|
||||
|
||||
/** @since 0.8.1 */
|
||||
|
||||
131
apps/i2psnark/java/src/org/klomp/snark/web/MimeTypes.java
Normal file
131
apps/i2psnark/java/src/org/klomp/snark/web/MimeTypes.java
Normal file
@@ -0,0 +1,131 @@
|
||||
// ========================================================================
|
||||
// Copyright 2000-2005 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ========================================================================
|
||||
|
||||
package org.klomp.snark.web;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Based on MimeTypes from Jetty 6.1.26, heavily simplified
|
||||
* and modified to remove all dependencies on Jetty libs.
|
||||
*
|
||||
* Supports mime types only, not encodings.
|
||||
* Does not support a default "*" mapping.
|
||||
*
|
||||
* This is only for local mappings.
|
||||
* Caller should use getServletContext().getMimeType() if this returns null.
|
||||
*
|
||||
*
|
||||
* ------------------------------------------------------------
|
||||
*
|
||||
* @author Greg Wilkins
|
||||
*
|
||||
* @since Jetty 7
|
||||
*/
|
||||
class MimeTypes
|
||||
{
|
||||
|
||||
private final Map<String, String> _mimeMap;
|
||||
|
||||
public MimeTypes() {
|
||||
_mimeMap = new ConcurrentHashMap();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param resourcePath A Map of file extension to mime-type.
|
||||
*/
|
||||
public void loadMimeMap(String resourcePath) {
|
||||
loadMimeMap(_mimeMap, resourcePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries both webapp and system class loader, since Jetty blocks
|
||||
* its classes from the webapp class loader.
|
||||
*/
|
||||
private static void loadMimeMap(Map<String, String> map, String resourcePath) {
|
||||
try
|
||||
{
|
||||
ResourceBundle mime;
|
||||
try {
|
||||
mime = ResourceBundle.getBundle(resourcePath);
|
||||
} catch(MissingResourceException e) {
|
||||
// Jetty 7 webapp classloader blocks jetty classes
|
||||
// http://wiki.eclipse.org/Jetty/Reference/Jetty_Classloading
|
||||
//System.out.println("No mime types loaded from " + resourcePath + ", trying system classloader");
|
||||
mime = ResourceBundle.getBundle(resourcePath, Locale.getDefault(), ClassLoader.getSystemClassLoader());
|
||||
}
|
||||
Enumeration<String> i = mime.getKeys();
|
||||
while(i.hasMoreElements())
|
||||
{
|
||||
String ext = i.nextElement();
|
||||
String m = mime.getString(ext);
|
||||
map.put(ext.toLowerCase(Locale.US), m);
|
||||
}
|
||||
//System.out.println("Loaded " + map.size() + " mime types from " + resourcePath);
|
||||
} catch(MissingResourceException e) {
|
||||
//System.out.println("No mime types loaded from " + resourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the MIME type by filename extension.
|
||||
*
|
||||
* Returns ONLY local mappings.
|
||||
* Caller should use getServletContext().getMimeType() if this returns null.
|
||||
*
|
||||
* @param filename A file name
|
||||
* @return MIME type matching the longest dot extension of the
|
||||
* file name.
|
||||
*/
|
||||
public String getMimeByExtension(String filename)
|
||||
{
|
||||
String type=null;
|
||||
|
||||
if (filename!=null)
|
||||
{
|
||||
int i=-1;
|
||||
while(type==null)
|
||||
{
|
||||
i=filename.indexOf(".",i+1);
|
||||
|
||||
if (i<0 || i>=filename.length())
|
||||
break;
|
||||
|
||||
String ext=filename.substring(i+1).toLowerCase(Locale.US);
|
||||
type = _mimeMap.get(ext);
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Set a mime mapping
|
||||
* @param extension
|
||||
* @param type
|
||||
*/
|
||||
public void addMimeMapping(String extension, String type)
|
||||
{
|
||||
_mimeMap.put(extension.toLowerCase(Locale.US), type);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import java.io.File;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.FileUtil;
|
||||
|
||||
import org.mortbay.jetty.Server;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
||||
public class RunStandalone {
|
||||
static {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.klomp.snark.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* Treat a writer as an output stream. Quick 'n dirty, none
|
||||
* of that "intarnasheeonaleyzayshun" stuff. So we can treat
|
||||
* the jsp's PrintWriter as an OutputStream
|
||||
*
|
||||
* @since Jetty 7 copied from routerconsole
|
||||
*/
|
||||
class WriterOutputStream extends OutputStream {
|
||||
private final Writer _writer;
|
||||
|
||||
public WriterOutputStream(Writer writer) { _writer = writer; }
|
||||
public void write(int b) throws IOException { _writer.write(b); }
|
||||
}
|
||||
22
apps/i2psnark/mime.properties
Normal file
22
apps/i2psnark/mime.properties
Normal file
@@ -0,0 +1,22 @@
|
||||
7z = application/x-7z-compressed
|
||||
ape = audio/x-monkeys-audio
|
||||
dmg = application/apple-diskimage
|
||||
flac = audio/flac
|
||||
flv = video/x-flv
|
||||
iso = application/x-iso9660-image
|
||||
m4a = audio/mp4a-latm
|
||||
m4v = video/x-m4v
|
||||
mkv = video/x-matroska
|
||||
mp4 = video/mp4
|
||||
mpc = audio/x-musepack
|
||||
nfo = text/plain
|
||||
ogm = video/ogg
|
||||
ogv = video/ogg
|
||||
oga = audio/ogg
|
||||
rar = application/x-rar-compressed
|
||||
su2 = application/zip
|
||||
sud = application/zip
|
||||
txt = text/plain
|
||||
war = application/java-archive
|
||||
wma = audio/x-ms-wma
|
||||
wmv = video/x-ms-wmv
|
||||
@@ -65,10 +65,12 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
_buf1[0] = (byte)c;
|
||||
write(_buf1, 0, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte buf[]) throws IOException {
|
||||
write(buf, 0, buf.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte buf[], int off, int len) throws IOException {
|
||||
if (_headerWritten) {
|
||||
@@ -181,6 +183,7 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
_gzip = true;
|
||||
} else if ("proxy-authenticate".equals(lcKey)) {
|
||||
// filter this hop-by-hop header; outproxy authentication must be configured in I2PTunnelHTTPClient
|
||||
// see e.g. http://blog.c22.cc/2013/03/11/privoxy-proxy-authentication-credential-exposure-cve-2013-2503/
|
||||
} else {
|
||||
if ("content-length".equals(lcKey)) {
|
||||
// save for compress decision on server side
|
||||
@@ -272,7 +275,7 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
while ( (read = _in.read(buf)) != -1) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Read " + read + " and writing it to the browser/streams");
|
||||
; _out.write(buf, 0, read);
|
||||
_out.write(buf, 0, read);
|
||||
_out.flush();
|
||||
written += read;
|
||||
}
|
||||
|
||||
@@ -162,6 +162,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"<html><body><H1>I2P ERROR: NON-HTTP PROTOCOL</H1>" +
|
||||
"The request uses a bad protocol. " +
|
||||
"The I2P HTTP Proxy supports http:// requests ONLY. Other protocols such as https:// and ftp:// are not allowed.<BR>").getBytes();
|
||||
private final static byte[] ERR_BAD_URI =
|
||||
("HTTP/1.1 403 Bad URI\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
"Cache-control: no-cache\r\n" +
|
||||
"\r\n" +
|
||||
"<html><body><H1>I2P ERROR: INVALID REQUEST URI</H1>" +
|
||||
"The request URI is invalid, and probably contains illegal characters. " +
|
||||
"If you clicked e.g. a forum link, check the end of the URI for any characters the browser has mistakenly added on.<BR>").getBytes();
|
||||
private final static byte[] ERR_LOCALHOST =
|
||||
("HTTP/1.1 403 Access Denied\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
@@ -428,7 +436,12 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
if(_log.shouldLog(Log.WARN)) {
|
||||
_log.warn(getPrefix(requestId) + "Bad request [" + request + "]", use);
|
||||
}
|
||||
break;
|
||||
if(out != null) {
|
||||
out.write(getErrorPage("baduri", ERR_BAD_URI));
|
||||
writeFooter(out);
|
||||
}
|
||||
s.close();
|
||||
return;
|
||||
}
|
||||
method = params[0];
|
||||
String protocolVersion = params[2];
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
<link href="<%=editBean.getTheme()%>i2ptunnel.css" rel="stylesheet" type="text/css" />
|
||||
<% }
|
||||
%>
|
||||
<style type='text/css'>
|
||||
input.default { width: 1px; height: 1px; visibility: hidden; }
|
||||
</style>
|
||||
</head>
|
||||
<body id="tunnelEditPage">
|
||||
<div id="pageHeader">
|
||||
@@ -55,6 +58,7 @@
|
||||
<input type="hidden" name="tunnel" value="<%=request.getParameter("tunnel")%>" />
|
||||
<input type="hidden" name="nonce" value="<%=editBean.getNextNonce()%>" />
|
||||
<input type="hidden" name="type" value="<%=tunnelType%>" />
|
||||
<input type="submit" class="default" name="action" value="Save changes" />
|
||||
</div>
|
||||
|
||||
<div class="separator">
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
<link href="<%=editBean.getTheme()%>i2ptunnel.css" rel="stylesheet" type="text/css" />
|
||||
<% }
|
||||
%>
|
||||
<style type='text/css'>
|
||||
input.default { width: 1px; height: 1px; visibility: hidden; }
|
||||
</style>
|
||||
</head>
|
||||
<body id="tunnelEditPage">
|
||||
<div id="pageHeader">
|
||||
@@ -55,6 +58,7 @@
|
||||
<input type="hidden" name="tunnel" value="<%=request.getParameter("tunnel")%>" />
|
||||
<input type="hidden" name="nonce" value="<%=editBean.getNextNonce()%>" />
|
||||
<input type="hidden" name="type" value="<%=tunnelType%>" />
|
||||
<input type="submit" class="default" name="action" value="Save changes" />
|
||||
</div>
|
||||
|
||||
<div class="separator">
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
<%
|
||||
if (indexBean.isServerTargetLinkValid(curServer)) {
|
||||
%>
|
||||
<a href="http://<%=indexBean.getServerTarget(curServer)%>/" title="Test HTTP server, bypassing I2P"><%=indexBean.getServerTarget(curServer)%></a>
|
||||
<a href="http://<%=indexBean.getServerTarget(curServer)%>/" title="Test HTTP server, bypassing I2P" target="_top"><%=indexBean.getServerTarget(curServer)%></a>
|
||||
<%
|
||||
} else {
|
||||
%><%=indexBean.getServerTarget(curServer)%>
|
||||
@@ -127,7 +127,7 @@
|
||||
<%
|
||||
if (("httpserver".equals(indexBean.getInternalType(curServer)) || ("httpbidirserver".equals(indexBean.getInternalType(curServer)))) && indexBean.getTunnelStatus(curServer) == IndexBean.RUNNING) {
|
||||
%><label><%=intl._("Preview")%>:</label>
|
||||
<a class="control" title="Test HTTP server through I2P" href="http://<%=indexBean.getDestHashBase32(curServer)%>.b32.i2p" target="_parent"><%=intl._("Preview")%></a>
|
||||
<a class="control" title="Test HTTP server through I2P" href="http://<%=indexBean.getDestHashBase32(curServer)%>.b32.i2p" target="_top"><%=intl._("Preview")%></a>
|
||||
<%
|
||||
} else if (indexBean.getTunnelStatus(curServer) == IndexBean.RUNNING) {
|
||||
%><span class="text"><%=intl._("Base32 Address")%>:<br /><%=indexBean.getDestHashBase32(curServer)%>.b32.i2p</span>
|
||||
|
||||
3
apps/jetty/README-i2p.txt
Normal file
3
apps/jetty/README-i2p.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
jetty-distribution-xxx is only what we need out of jetty-distribution-xxx.zip.
|
||||
|
||||
NOTICE and LICENSE files moved to ../../../licenses
|
||||
@@ -1,27 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="all" name="jetty">
|
||||
|
||||
<property name="jetty.ver" value="6.1.26" />
|
||||
<property name="jetty.base" value="jetty-${jetty.ver}" />
|
||||
<property name="jetty.sha1" value="9485913f1a1945a849a90f1a34853d22350bc524" />
|
||||
<property name="jetty.ver" value="7.6.10.v20130312" />
|
||||
<property name="jetty.base" value="jetty-distribution-${jetty.ver}" />
|
||||
<property name="jetty.sha1" value="8c0ff0ad83756de118f5af71b88ca9c78ebf6420" />
|
||||
<property name="jetty.filename" value="${jetty.base}.zip" />
|
||||
<property name="jetty.url" value="http://dist.codehaus.org/jetty/${jetty.base}/${jetty.filename}" />
|
||||
<property name="jetty.url" value="http://download.eclipse.org/jetty/${jetty.ver}/dist/${jetty.filename}" />
|
||||
<property name="verified.filename" value="verified.txt" />
|
||||
<property name="javac.compilerargs" value="" />
|
||||
<property name="tomcat.lib" value="apache-tomcat-deployer/lib" />
|
||||
|
||||
<target name="all" depends="build" />
|
||||
|
||||
<!--
|
||||
<!--
|
||||
- We now check in the jars we need to ${jetty.base}, so
|
||||
- fetchJettylib, verifyJettylib, and extractJettylib are not used unless
|
||||
- updating to a new Jetty version by changing ${jetty.ver} and ${jetty.sha1} above.
|
||||
-->
|
||||
|
||||
<target name="ensureJettylib" >
|
||||
<available property="jetty.zip.available" file="${jetty.filename}" type="file" />
|
||||
<available property="jetty.zip.extracted" file="jettylib" type="dir" />
|
||||
<ant target="fetchJettylib" />
|
||||
<ant target="verifyJettylib" />
|
||||
<ant target="extractJettylib" />
|
||||
<available property="jetty.zip.extracted" file="${jetty.base}" type="dir" />
|
||||
<condition property="jetty.zip.available" >
|
||||
<or>
|
||||
<istrue value="${jetty.zip.extracted}" />
|
||||
<available file="${jetty.filename}" type="file" />
|
||||
</or>
|
||||
</condition>
|
||||
<ant target="copyJettylib" />
|
||||
</target>
|
||||
-->
|
||||
<!--
|
||||
<target name="ensureJettylib" depends="extractJettylib" />
|
||||
-->
|
||||
|
||||
<target name="fetchJettylib" unless="jetty.zip.available" >
|
||||
<echo message="It seems that you don't have '${jetty.filename}' deployed." />
|
||||
@@ -48,13 +57,16 @@
|
||||
</target>
|
||||
|
||||
<condition property="verified.already" >
|
||||
<and>
|
||||
<available file="${jetty.filename}" />
|
||||
<uptodate property="foo.bar.baz" srcfile="${jetty.filename}" targetfile="${verified.filename}" />
|
||||
</and>
|
||||
<or>
|
||||
<istrue value="${jetty.zip.extracted}" />
|
||||
<and>
|
||||
<available file="${jetty.filename}" />
|
||||
<uptodate property="foo.bar.baz" srcfile="${jetty.filename}" targetfile="${verified.filename}" />
|
||||
</and>
|
||||
</or>
|
||||
</condition>
|
||||
|
||||
<target name="verifyJettylib" unless="verified.already" >
|
||||
<target name="verifyJettylib" depends="fetchJettylib" unless="verified.already" >
|
||||
<condition property="jetty.zip.verified" >
|
||||
<checksum file="${jetty.filename}" algorithm="SHA" property="${jetty.sha1}" />
|
||||
</condition>
|
||||
@@ -68,43 +80,61 @@
|
||||
<touch file="${verified.filename}" />
|
||||
</target>
|
||||
|
||||
<!--
|
||||
<target name="extractJettylib" unless="jetty.zip.extracted" >
|
||||
-->
|
||||
<target name="extractJettylib" >
|
||||
<target name="extractJettylib" depends="verifyJettylib" unless="jetty.zip.extracted" >
|
||||
<!-- for .tgz -->
|
||||
<!--
|
||||
<gunzip src="${jetty.filename}" dest="jetty.tar" />
|
||||
<untar src="jetty.tar" dest="." />
|
||||
-->
|
||||
<!-- for .zip -->
|
||||
<!--
|
||||
<unzip src="${jetty.filename}" dest="." />
|
||||
-->
|
||||
</target>
|
||||
|
||||
<target name="copyJettylib" depends="extractJettylib" >
|
||||
<mkdir dir="jettylib" />
|
||||
<!-- We copy everything to names without the version numbers so we
|
||||
can update them later. Where there was something similar in Jetty 5,
|
||||
we use the same names so they will overwrite the Jetty 5 jar on upgrade.
|
||||
can update them later. Where there was something similar in Jetty 5/6,
|
||||
we use the same names so they will overwrite the Jetty 5/6 jar on upgrade.
|
||||
Otherwise we use the same name as the symlink in Ubuntu /usr/share/java.
|
||||
Reasons for inclusion:
|
||||
start.jar: Needed for clients.config startup of eepsites
|
||||
jetty-util-xxx.jar: LifeCycle (base class for stuff), URIUtil (used in i2psnark)
|
||||
jetty-sslengine-xxx.jar: SSL NIO Connector for console
|
||||
jetty-java5-threadpool-xxx.jar: Concurrent thread pool for eepsite
|
||||
jetty-deploy, -http, -io, -security, -servlet, -webapp: All split out from main server jar in Jetty 7
|
||||
jetty-continuation-xxx.jar: Needed? Useful?
|
||||
jetty-servlets-xxx.jar: Needed for CGI for eepsite
|
||||
jetty-sslengine-xxx.jar: Old Jetty 6, now a dummy
|
||||
jetty-java5-threadpool-xxx.jar: Old Jetty 6, now a dummy
|
||||
glassfish 2.1: Not used, too old, see Tomcat below.
|
||||
jetty-rewrite-handler: Not used by I2P, but only 20KB and could be useful for eepsites
|
||||
jetty-management: Not used by I2P, but only 34KB and could be useful for eepsites, and we bundled it with Jetty 5
|
||||
All of these are available in the Ubuntu packages libjetty-java and libjetty-extra-java
|
||||
-->
|
||||
<copy preservelastmodified="true" file="${jetty.base}/start.jar" tofile="jettylib/jetty-start.jar" />
|
||||
<copy file="${jetty.base}/lib/${jetty.base}.jar" tofile="jettylib/org.mortbay.jetty.jar" />
|
||||
<copy file="${jetty.base}/lib/jetty-server-${jetty.ver}.jar" tofile="jettylib/org.mortbay.jetty.jar" />
|
||||
<copy preservelastmodified="true" file="${jetty.base}/lib/jetty-continuation-${jetty.ver}.jar" tofile="jettylib/jetty-continuation.jar" />
|
||||
<copy preservelastmodified="true" file="${jetty.base}/lib/jetty-deploy-${jetty.ver}.jar" tofile="jettylib/jetty-deploy.jar" />
|
||||
<copy preservelastmodified="true" file="${jetty.base}/lib/jetty-http-${jetty.ver}.jar" tofile="jettylib/jetty-http.jar" />
|
||||
<copy preservelastmodified="true" file="${jetty.base}/lib/jetty-io-${jetty.ver}.jar" tofile="jettylib/jetty-io.jar" />
|
||||
<copy preservelastmodified="true" file="${jetty.base}/lib/jetty-jmx-${jetty.ver}.jar" tofile="jettylib/org.mortbay.jmx.jar" />
|
||||
<copy preservelastmodified="true" file="${jetty.base}/lib/jetty-rewrite-${jetty.ver}.jar" tofile="jettylib/jetty-rewrite-handler.jar" />
|
||||
<copy preservelastmodified="true" file="${jetty.base}/lib/jetty-security-${jetty.ver}.jar" tofile="jettylib/jetty-security.jar" />
|
||||
<copy preservelastmodified="true" file="${jetty.base}/lib/jetty-servlet-${jetty.ver}.jar" tofile="jettylib/jetty-servlet.jar" />
|
||||
<copy preservelastmodified="true" file="${jetty.base}/lib/jetty-servlets-${jetty.ver}.jar" tofile="jettylib/jetty-servlets.jar" />
|
||||
<copy preservelastmodified="true" file="${jetty.base}/lib/jetty-util-${jetty.ver}.jar" tofile="jettylib/jetty-util.jar" />
|
||||
<copy preservelastmodified="true" file="${jetty.base}/lib/ext/jetty-java5-threadpool-${jetty.ver}.jar" tofile="jettylib/jetty-java5-threadpool.jar" />
|
||||
<copy preservelastmodified="true" file="${jetty.base}/lib/ext/jetty-rewrite-handler-${jetty.ver}.jar" tofile="jettylib/jetty-rewrite-handler.jar" />
|
||||
<copy preservelastmodified="true" file="${jetty.base}/lib/ext/jetty-sslengine-${jetty.ver}.jar" tofile="jettylib/jetty-sslengine.jar" />
|
||||
<copy preservelastmodified="true" file="${jetty.base}/lib/management/jetty-management-${jetty.ver}.jar" tofile="jettylib/org.mortbay.jmx.jar" />
|
||||
<delete file="jetty.tar" />
|
||||
<copy preservelastmodified="true" file="${jetty.base}/lib/jetty-webapp-${jetty.ver}.jar" tofile="jettylib/jetty-webapp.jar" />
|
||||
<copy preservelastmodified="true" file="${jetty.base}/lib/jetty-xml-${jetty.ver}.jar" tofile="jettylib/jetty-xml.jar" />
|
||||
<jar destfile="jettylib/jetty-java5-threadpool.jar" >
|
||||
<manifest>
|
||||
<attribute name="Note" value="Intentionally empty" />
|
||||
</manifest>
|
||||
</jar>
|
||||
<jar destfile="jettylib/jetty-sslengine.jar" >
|
||||
<manifest>
|
||||
<attribute name="Note" value="Intentionally empty" />
|
||||
</manifest>
|
||||
</jar>
|
||||
<!--
|
||||
<delete file="jetty.tar" />
|
||||
<delete dir="${jetty.base}" />
|
||||
-->
|
||||
<!-- commons-logging.jar not in Jetty 6 but we have it in launch4j so copy it over,
|
||||
@@ -155,15 +185,38 @@
|
||||
<target name="build" depends="jar" />
|
||||
|
||||
<target name="builddep" />
|
||||
<target name="compile" depends="builddep, ensureJettylib" >
|
||||
|
||||
<condition property="depend.available">
|
||||
<typefound name="depend" />
|
||||
</condition>
|
||||
|
||||
<target name="depend" if="depend.available">
|
||||
<depend
|
||||
cache="../../build"
|
||||
srcdir="./java/src"
|
||||
destdir="./build/obj" >
|
||||
<classpath>
|
||||
<pathelement location="../../../core/java/build/i2p.jar" />
|
||||
<pathelement location="./jettylib/commons-logging.jar" />
|
||||
<pathelement location="./jettylib/org.mortbay.jetty.jar" />
|
||||
<pathelement location="./jettylib/javax.servlet.jar" />
|
||||
<pathelement location="./jettylib/jetty-http.jar" />
|
||||
<pathelement location="./jettylib/jetty-io.jar" />
|
||||
<pathelement location="./jettylib/jetty-util.jar" />
|
||||
<pathelement location="./jettylib/jetty-xml.jar" />
|
||||
</classpath>
|
||||
</depend>
|
||||
</target>
|
||||
|
||||
<target name="compile" depends="builddep, ensureJettylib, depend" >
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/obj" />
|
||||
<javac
|
||||
srcdir="./java/src"
|
||||
debug="true" source="1.5" target="1.5"
|
||||
debug="true" deprecation="on" source="1.5" target="1.5"
|
||||
destdir="./build/obj"
|
||||
includeAntRuntime="false"
|
||||
classpath="../../core/java/build/i2p.jar:./jettylib/commons-logging.jar:./jettylib/javax.servlet.jar:./jettylib/org.mortbay.jetty.jar:./jettylib/jetty-util.jar" >
|
||||
classpath="../../core/java/build/i2p.jar:./jettylib/commons-logging.jar:./jettylib/javax.servlet.jar:./jettylib/org.mortbay.jetty.jar:./jettylib/jetty-http.jar:./jettylib/jetty-io.jar:./jettylib/jetty-util.jar:./jettylib/jetty-xml.jar" >
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
@@ -17,13 +17,13 @@ package net.i2p.jetty;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import org.mortbay.jetty.Server;
|
||||
import org.mortbay.log.Logger;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/**
|
||||
* Modified from Jetty 6.1.26 StdErrLog.java and Slf4jLog.java
|
||||
*
|
||||
* Usage: org.mortbay.log.Log.setLog(new I2PLogger(ctx));
|
||||
* Usage: org.eclipse.log.Log.setLog(new I2PLogger(ctx));
|
||||
*
|
||||
* @since Jetty 6
|
||||
*/
|
||||
@@ -182,5 +182,80 @@ public class I2PLogger implements Logger
|
||||
return "I2PLogger";
|
||||
}
|
||||
|
||||
/**
|
||||
* @since Jetty 7
|
||||
*/
|
||||
public void ignore(Throwable ignored)
|
||||
{
|
||||
debug("IGNORED", ignored);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since Jetty 7
|
||||
*/
|
||||
public void debug(Throwable thrown)
|
||||
{
|
||||
debug("", thrown);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since Jetty 7
|
||||
*/
|
||||
public void debug(String msg, Object... args)
|
||||
{
|
||||
Object a1 = args.length > 0 ? args[0] : null;
|
||||
Object a2 = args.length > 1 ? args[1] : null;
|
||||
debug(msg, a1, a2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since Jetty 7
|
||||
*/
|
||||
public void info(Throwable thrown)
|
||||
{
|
||||
info("", thrown);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since Jetty 7
|
||||
*/
|
||||
public void info(String msg, Object... args)
|
||||
{
|
||||
Object a1 = args.length > 0 ? args[0] : null;
|
||||
Object a2 = args.length > 1 ? args[1] : null;
|
||||
info(msg, a1, a2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since Jetty 7
|
||||
*/
|
||||
public void info(String msg, Throwable th)
|
||||
{
|
||||
_log.info(msg,th);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since Jetty 7
|
||||
*/
|
||||
public void warn(Throwable thrown)
|
||||
{
|
||||
warn("", thrown);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since Jetty 7
|
||||
*/
|
||||
public void warn(String msg, Object... args)
|
||||
{
|
||||
Object a1 = args.length > 0 ? args[0] : null;
|
||||
Object a2 = args.length > 1 ? args[1] : null;
|
||||
warn(msg, a1, a2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since Jetty 7
|
||||
*/
|
||||
public String getName() {
|
||||
return "net.i2p.jetty.I2PLogger";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,18 +24,18 @@ import java.util.TimeZone;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.mortbay.component.AbstractLifeCycle;
|
||||
import org.mortbay.jetty.HttpHeaders;
|
||||
import org.mortbay.jetty.Request;
|
||||
import org.mortbay.jetty.RequestLog;
|
||||
import org.mortbay.jetty.Response;
|
||||
import org.mortbay.jetty.servlet.PathMap;
|
||||
import org.mortbay.log.Log;
|
||||
import org.mortbay.util.DateCache;
|
||||
import org.mortbay.util.RolloverFileOutputStream;
|
||||
import org.mortbay.util.StringUtil;
|
||||
import org.mortbay.util.TypeUtil;
|
||||
import org.mortbay.util.Utf8StringBuffer;
|
||||
import org.eclipse.jetty.http.HttpHeaders;
|
||||
import org.eclipse.jetty.http.PathMap;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.RequestLog;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.util.DateCache;
|
||||
import org.eclipse.jetty.util.RolloverFileOutputStream;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.Utf8StringBuilder;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
||||
/**
|
||||
* This {@link RequestLog} implementation outputs logs in the pseudo-standard NCSA common log format.
|
||||
@@ -56,8 +56,6 @@ import org.mortbay.util.Utf8StringBuffer;
|
||||
*
|
||||
* So that we will work with system Jetty 6 packages, we just copy the whole thing
|
||||
* and modify log() as required.
|
||||
* We leave the package as org.mortbay.http for compatibility with old
|
||||
* jetty.xml files.
|
||||
*
|
||||
* @author Greg Wilkins
|
||||
* @author Nigel Canonizado
|
||||
@@ -259,13 +257,13 @@ public class I2PRequestLog extends AbstractLifeCycle implements RequestLog
|
||||
if (_fileOut == null)
|
||||
return;
|
||||
|
||||
Utf8StringBuffer u8buf;
|
||||
StringBuffer buf;
|
||||
Utf8StringBuilder u8buf;
|
||||
StringBuilder buf;
|
||||
synchronized(_writer)
|
||||
{
|
||||
int size=_buffers.size();
|
||||
u8buf = size==0?new Utf8StringBuffer(160):(Utf8StringBuffer)_buffers.remove(size-1);
|
||||
buf = u8buf.getStringBuffer();
|
||||
u8buf = size==0?new Utf8StringBuilder(160):(Utf8StringBuilder)_buffers.remove(size-1);
|
||||
buf = u8buf.getStringBuilder();
|
||||
}
|
||||
|
||||
synchronized(buf) // for efficiency until we can use StringBuilder
|
||||
@@ -398,7 +396,7 @@ public class I2PRequestLog extends AbstractLifeCycle implements RequestLog
|
||||
if (_logLatency)
|
||||
{
|
||||
_writer.write(' ');
|
||||
_writer.write(TypeUtil.toString(System.currentTimeMillis() - request.getTimeStamp()));
|
||||
_writer.write(Long.toString(System.currentTimeMillis() - request.getTimeStamp()));
|
||||
}
|
||||
|
||||
_writer.write(StringUtil.__LINE_SEPARATOR);
|
||||
|
||||
@@ -19,15 +19,16 @@ package net.i2p.jetty;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.*;
|
||||
import static net.i2p.app.ClientAppState.*;
|
||||
|
||||
import org.mortbay.component.LifeCycle;
|
||||
import org.mortbay.resource.Resource;
|
||||
import org.mortbay.xml.XmlConfiguration;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.xml.XmlConfiguration;
|
||||
|
||||
/**
|
||||
* Start Jetty where the args are one or more XML files.
|
||||
@@ -73,8 +74,11 @@ public class JettyStart implements ClientApp {
|
||||
XmlConfiguration configuration = new XmlConfiguration(Resource.newResource(args[i]).getURL());
|
||||
if (last!=null)
|
||||
configuration.getIdMap().putAll(last.getIdMap());
|
||||
if (properties.size()>0)
|
||||
configuration.setProperties(properties);
|
||||
if (properties.size()>0) {
|
||||
// to avoid compiler errror
|
||||
Map foo = configuration.getProperties();
|
||||
foo.putAll(properties);
|
||||
}
|
||||
Object o = configuration.configure();
|
||||
if (o instanceof LifeCycle)
|
||||
_jettys.add((LifeCycle)o);
|
||||
@@ -114,6 +118,7 @@ public class JettyStart implements ClientApp {
|
||||
}
|
||||
}
|
||||
changeState(RUNNING);
|
||||
_mgr.register(JettyStart.this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
This is only what we need out of the 25 MB Jetty-6.1.26.zip.
|
||||
|
||||
NOTICE and LICENSE files moved to ../../../licenses
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
apps/jetty/jetty-distribution-7.6.10.v20130312/start.jar
Normal file
BIN
apps/jetty/jetty-distribution-7.6.10.v20130312/start.jar
Normal file
Binary file not shown.
@@ -63,9 +63,13 @@
|
||||
<pathelement location="../../../core/java/build/i2p.jar" />
|
||||
<pathelement location="../../../router/java/build/router.jar" />
|
||||
<pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-http.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-io.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-security.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-servlet.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-servlets.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-util.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-sslengine.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-java5-threadpool.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-webapp.jar" />
|
||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jsp-api.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-i2p.jar" />
|
||||
|
||||
@@ -101,6 +101,7 @@ public class ConsoleUpdateManager implements UpdateManager {
|
||||
notifyInstalled(NEWS, "", Long.toString(NewsHelper.lastUpdated(_context)));
|
||||
notifyInstalled(ROUTER_SIGNED, "", RouterVersion.VERSION);
|
||||
// hack to init from the current news file... do this before we register Updaters
|
||||
// This will not kick off any Updaters as none are yet registered
|
||||
(new NewsFetcher(_context, this, Collections.EMPTY_LIST)).checkForUpdates();
|
||||
for (String plugin : PluginStarter.getPlugins()) {
|
||||
Properties props = PluginStarter.pluginProperties(_context, plugin);
|
||||
@@ -608,6 +609,7 @@ public class ConsoleUpdateManager implements UpdateManager {
|
||||
|
||||
/**
|
||||
* Called by the Updater, either after check() was called, or it found out on its own.
|
||||
* Use this if there is only one UpdateMethod; otherwise use the Map method below.
|
||||
*
|
||||
* @param newsSource who told us
|
||||
* @param id plugin name for plugins, ignored otherwise
|
||||
@@ -620,50 +622,95 @@ public class ConsoleUpdateManager implements UpdateManager {
|
||||
UpdateType type, String id,
|
||||
UpdateMethod method, List<URI> updateSources,
|
||||
String newVersion, String minVersion) {
|
||||
return notifyVersionAvailable(task, newsSource, type, id,
|
||||
Collections.singletonMap(method, updateSources),
|
||||
newVersion, minVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the Checker, either after check() was called, or it found out on its own.
|
||||
* Checkers must use this method if there are multiple UpdateMethods discoverd simultaneously.
|
||||
*
|
||||
* @param newsSource who told us
|
||||
* @param id plugin name for plugins, ignored otherwise
|
||||
* @param sourceMap Mapping of methods to sources
|
||||
* @param newVersion The new version available
|
||||
* @param minVersion The minimum installed version to be able to update to newVersion
|
||||
* @return true if we didn't know already
|
||||
* @since 0.9.6
|
||||
*/
|
||||
public boolean notifyVersionAvailable(UpdateTask task, URI newsSource,
|
||||
UpdateType type, String id,
|
||||
Map<UpdateMethod, List<URI>> sourceMap,
|
||||
String newVersion, String minVersion) {
|
||||
if (type == NEWS) {
|
||||
// shortcut
|
||||
notifyInstalled(NEWS, "", newVersion);
|
||||
return true;
|
||||
}
|
||||
UpdateItem ui = new UpdateItem(type, id);
|
||||
VersionAvailable newVA = new VersionAvailable(newVersion, minVersion, method, updateSources);
|
||||
Version old = _installed.get(ui);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("notifyVersionAvailable " + ui + ' ' + newVA + " old: " + old);
|
||||
if (old != null && old.compareTo(newVA) >= 0) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(ui.toString() + ' ' + old + " already installed");
|
||||
return false;
|
||||
}
|
||||
old = _downloaded.get(ui);
|
||||
if (old != null && old.compareTo(newVA) >= 0) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(ui.toString() + ' ' + old + " already downloaded");
|
||||
return false;
|
||||
}
|
||||
VersionAvailable oldVA = _available.get(ui);
|
||||
if (oldVA != null) {
|
||||
int comp = oldVA.compareTo(newVA);
|
||||
if (comp > 0) {
|
||||
boolean shouldUpdate = false;
|
||||
for (Map.Entry<UpdateMethod, List<URI>> e : sourceMap.entrySet()) {
|
||||
UpdateMethod method = e.getKey();
|
||||
List<URI> updateSources = e.getValue();
|
||||
|
||||
VersionAvailable newVA = new VersionAvailable(newVersion, minVersion, method, updateSources);
|
||||
Version old = _installed.get(ui);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("notifyVersionAvailable " + ui + ' ' + newVA + " old: " + old);
|
||||
if (old != null && old.compareTo(newVA) >= 0) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(ui.toString() + ' ' + oldVA + " already available");
|
||||
_log.warn(ui.toString() + ' ' + old + " already installed");
|
||||
// don't bother updating sources
|
||||
return false;
|
||||
}
|
||||
if (comp == 0) {
|
||||
if (oldVA.sourceMap.putIfAbsent(method, updateSources) == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(ui.toString() + ' ' + oldVA + " updated with new source method");
|
||||
} else {
|
||||
old = _downloaded.get(ui);
|
||||
if (old != null && old.compareTo(newVA) >= 0) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(ui.toString() + ' ' + old + " already downloaded");
|
||||
// don't bother updating sources
|
||||
return false;
|
||||
}
|
||||
VersionAvailable oldVA = _available.get(ui);
|
||||
if (oldVA != null) {
|
||||
int comp = oldVA.compareTo(newVA);
|
||||
if (comp > 0) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(ui.toString() + ' ' + oldVA + " already available");
|
||||
}
|
||||
return false;
|
||||
continue;
|
||||
} else if (comp == 0) {
|
||||
List<URI> oldSources = oldVA.sourceMap.putIfAbsent(method, updateSources);
|
||||
if (oldSources == null) {
|
||||
// merge with existing VersionAvailable
|
||||
// new method
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(ui.toString() + ' ' + oldVA + " updated with new source method");
|
||||
} else if (!oldSources.containsAll(updateSources)) {
|
||||
// merge with existing VersionAvailable
|
||||
// new sources to existing method
|
||||
for (URI uri : updateSources) {
|
||||
if (!oldSources.contains(uri)) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(ui.toString() + ' ' + oldVA + " adding " + uri + " to method " + method);
|
||||
oldSources.add(uri);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(ui.toString() + ' ' + oldVA + " already available");
|
||||
}
|
||||
continue;
|
||||
} // else new version is newer
|
||||
}
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(ui.toString() + ' ' + newVA + " now available");
|
||||
_available.put(ui, newVA);
|
||||
// Use the new VersionAvailable
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(ui.toString() + ' ' + newVA + " now available");
|
||||
_available.put(ui, newVA);
|
||||
shouldUpdate = true;
|
||||
}
|
||||
if (!shouldUpdate)
|
||||
return false;
|
||||
|
||||
String msg = null;
|
||||
switch (type) {
|
||||
@@ -679,7 +726,12 @@ public class ConsoleUpdateManager implements UpdateManager {
|
||||
if (shouldInstall() &&
|
||||
!(isUpdateInProgress(ROUTER_SIGNED) ||
|
||||
isUpdateInProgress(ROUTER_UNSIGNED))) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Updating " + ui + " after notify");
|
||||
update_fromCheck(type, id, DEFAULT_MAX_TIME);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Not updating " + ui + ", update disabled or in progress");
|
||||
}
|
||||
// ConfigUpdateHandler, SummaryHelper, SummaryBarRenderer handle status display
|
||||
break;
|
||||
@@ -890,7 +942,7 @@ public class ConsoleUpdateManager implements UpdateManager {
|
||||
}
|
||||
|
||||
/** from NewsFetcher */
|
||||
private boolean shouldInstall() {
|
||||
boolean shouldInstall() {
|
||||
String policy = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_POLICY);
|
||||
if ("notify".equals(policy) || NewsHelper.dontInstall(_context))
|
||||
return false;
|
||||
@@ -1049,7 +1101,9 @@ public class ConsoleUpdateManager implements UpdateManager {
|
||||
}
|
||||
|
||||
static String linkify(String url) {
|
||||
return "<a target=\"_blank\" href=\"" + url + "\"/>" + url + "</a>";
|
||||
String durl = url.length() <= 28 ? url :
|
||||
url.substring(0, 25) + "…";
|
||||
return "<a target=\"_blank\" href=\"" + url + "\"/>" + durl + "</a>";
|
||||
}
|
||||
|
||||
/** translate a string */
|
||||
|
||||
@@ -148,21 +148,21 @@ class NewsFetcher extends UpdateRunner {
|
||||
// TODO if minversion > our version, continue
|
||||
// and look for a second entry with clearnet URLs
|
||||
// TODO clearnet URLs, notify with HTTP_CLEARNET and/or HTTPS_CLEARNET
|
||||
_mgr.notifyVersionAvailable(this, _currentURI,
|
||||
ROUTER_SIGNED, "", HTTP,
|
||||
_mgr.getUpdateURLs(ROUTER_SIGNED, "", HTTP),
|
||||
ver, "");
|
||||
Map<UpdateMethod, List<URI>> sourceMap = new HashMap(4);
|
||||
sourceMap.put(HTTP, _mgr.getUpdateURLs(ROUTER_SIGNED, "", HTTP));
|
||||
String key = FileUtil.isPack200Supported() ? SU2_KEY : SUD_KEY;
|
||||
String murl = args.get(key);
|
||||
if (murl != null) {
|
||||
List<URI> uris = tokenize(murl);
|
||||
if (!uris.isEmpty()) {
|
||||
Collections.shuffle(uris, _context.random());
|
||||
_mgr.notifyVersionAvailable(this, _currentURI,
|
||||
ROUTER_SIGNED, "", TORRENT,
|
||||
uris, ver, "");
|
||||
sourceMap.put(TORRENT, uris);
|
||||
}
|
||||
}
|
||||
// notify about all sources at once
|
||||
_mgr.notifyVersionAvailable(this, _currentURI,
|
||||
ROUTER_SIGNED, "", sourceMap,
|
||||
ver, "");
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Our version is current");
|
||||
|
||||
@@ -39,6 +39,7 @@ class NewsTimerTask implements SimpleTimer.TimedEvent {
|
||||
private final RouterContext _context;
|
||||
private final Log _log;
|
||||
private final ConsoleUpdateManager _mgr;
|
||||
private boolean _firstRun = true;
|
||||
|
||||
private static final long INITIAL_DELAY = 5*60*1000;
|
||||
private static final long RUN_DELAY = 10*60*1000;
|
||||
@@ -64,7 +65,20 @@ class NewsTimerTask implements SimpleTimer.TimedEvent {
|
||||
// nonblocking
|
||||
fetchUnsignedHead();
|
||||
}
|
||||
} else if (_firstRun) {
|
||||
// This covers the case where we got a new news but then shut down before it
|
||||
// was successfully downloaded, and then restarted within the 36 hour delay
|
||||
// before fetching news again.
|
||||
// If we already know about a new version (from ConsoleUpdateManager calling
|
||||
// NewsFetcher.checkForUpdates() before any Updaters were registered),
|
||||
// this will fire off an update.
|
||||
// If disabled this does nothing.
|
||||
// TODO unsigned too?
|
||||
if (_mgr.shouldInstall() &&
|
||||
!_mgr.isCheckInProgress() && !_mgr.isUpdateInProgress())
|
||||
_mgr.update(ROUTER_SIGNED);
|
||||
}
|
||||
_firstRun = false;
|
||||
}
|
||||
|
||||
private boolean shouldFetchNews() {
|
||||
|
||||
@@ -11,13 +11,16 @@ import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.app.ClientApp;
|
||||
import net.i2p.app.ClientAppState;
|
||||
import net.i2p.router.client.ClientManagerFacadeImpl;
|
||||
import net.i2p.router.startup.ClientAppConfig;
|
||||
import net.i2p.router.startup.LoadClientAppsJob;
|
||||
import net.i2p.router.startup.RouterAppManager;
|
||||
import net.i2p.router.update.ConsoleUpdateManager;
|
||||
import static net.i2p.update.UpdateType.*;
|
||||
|
||||
import org.mortbay.jetty.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
|
||||
/**
|
||||
* Saves changes to clients.config or webapps.config
|
||||
@@ -214,10 +217,12 @@ public class ConfigClientsHandler extends FormHandler {
|
||||
|
||||
ClientAppConfig.writeClientAppConfig(_context, clients);
|
||||
addFormNotice(_("Client configuration saved successfully"));
|
||||
addFormNotice(_("Restart required to take effect"));
|
||||
//addFormNotice(_("Restart required to take effect"));
|
||||
}
|
||||
|
||||
// STUB for stopClient, not completed yet.
|
||||
/**
|
||||
* @since Implemented in 0.9.6 using ClientAppManager
|
||||
*/
|
||||
private void stopClient(int i) {
|
||||
List<ClientAppConfig> clients = ClientAppConfig.getClientApps(_context);
|
||||
if (i >= clients.size()) {
|
||||
@@ -225,10 +230,23 @@ public class ConfigClientsHandler extends FormHandler {
|
||||
return;
|
||||
}
|
||||
ClientAppConfig ca = clients.get(i);
|
||||
//
|
||||
// What do we do here?
|
||||
//
|
||||
addFormNotice(_("Client") + ' ' + _(ca.clientName) + ' ' + _("stopped") + '.');
|
||||
ClientApp clientApp = _context.clientAppManager().getClientApp(ca.className, LoadClientAppsJob.parseArgs(ca.args));
|
||||
if (clientApp != null && clientApp.getState() == ClientAppState.RUNNING) {
|
||||
try {
|
||||
// todo parseArgs(ca.stopArgs) ?
|
||||
clientApp.shutdown(null);
|
||||
addFormNotice(_("Client {0} stopped", ca.clientName));
|
||||
// Give a chance for status to update
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ie) {}
|
||||
} catch (Throwable t) {
|
||||
addFormError("Cannot stop client " + ca.className + ": " + t);
|
||||
_log.error("Error stopping client " + ca.className, t);
|
||||
}
|
||||
} else {
|
||||
addFormError("Cannot stop client " + i + ": " + ca.className);
|
||||
}
|
||||
}
|
||||
|
||||
private void startClient(int i) {
|
||||
@@ -239,7 +257,11 @@ public class ConfigClientsHandler extends FormHandler {
|
||||
}
|
||||
ClientAppConfig ca = clients.get(i);
|
||||
LoadClientAppsJob.runClient(ca.className, ca.clientName, LoadClientAppsJob.parseArgs(ca.args), _context, _log);
|
||||
addFormNotice(_("Client") + ' ' + _(ca.clientName) + ' ' + _("started") + '.');
|
||||
addFormNotice(_("Client {0} started", ca.clientName));
|
||||
// Give a chance for status to update
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
|
||||
private void deleteClient(int i) {
|
||||
@@ -250,7 +272,7 @@ public class ConfigClientsHandler extends FormHandler {
|
||||
}
|
||||
ClientAppConfig ca = clients.remove(i);
|
||||
ClientAppConfig.writeClientAppConfig(_context, clients);
|
||||
addFormNotice(_("Client") + ' ' + _(ca.clientName) + ' ' + _("deleted") + '.');
|
||||
addFormNotice(_("Client {0} deleted", ca.clientName));
|
||||
}
|
||||
|
||||
private void saveWebAppChanges() {
|
||||
|
||||
@@ -9,9 +9,13 @@ import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import net.i2p.app.ClientApp;
|
||||
import net.i2p.app.ClientAppState;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.router.client.ClientManagerFacadeImpl;
|
||||
import net.i2p.router.startup.ClientAppConfig;
|
||||
import net.i2p.router.startup.LoadClientAppsJob;
|
||||
import net.i2p.router.startup.RouterAppManager;
|
||||
import net.i2p.util.Addresses;
|
||||
|
||||
public class ConfigClientsHelper extends HelperBase {
|
||||
@@ -94,17 +98,33 @@ public class ConfigClientsHelper extends HelperBase {
|
||||
List<ClientAppConfig> clients = ClientAppConfig.getClientApps(_context);
|
||||
for (int cur = 0; cur < clients.size(); cur++) {
|
||||
ClientAppConfig ca = clients.get(cur);
|
||||
renderForm(buf, ""+cur, ca.clientName, false, !ca.disabled,
|
||||
boolean isConsole = ca.className.equals("net.i2p.router.web.RouterConsoleRunner");
|
||||
boolean showStart;
|
||||
boolean showStop;
|
||||
if (isConsole) {
|
||||
showStart = false;
|
||||
showStop = false;
|
||||
} else {
|
||||
ClientApp clientApp = _context.clientAppManager().getClientApp(ca.className, LoadClientAppsJob.parseArgs(ca.args));
|
||||
showStart = clientApp == null;
|
||||
showStop = clientApp != null && clientApp.getState() == ClientAppState.RUNNING;
|
||||
}
|
||||
renderForm(buf, ""+cur, ca.clientName,
|
||||
// urlify, enabled
|
||||
false, !ca.disabled,
|
||||
// read only
|
||||
// dangerous, but allow editing the console args too
|
||||
//"webConsole".equals(ca.clientName) || "Web console".equals(ca.clientName),
|
||||
false,
|
||||
// description, edit
|
||||
ca.className + ((ca.args != null) ? " " + ca.args : ""), (""+cur).equals(_edit),
|
||||
true, false,
|
||||
// Enable this one and comment out the false below once the stub is filled in.
|
||||
//!ca.disabled && !("webConsole".equals(ca.clientName) || "Web console".equals(ca.clientName)),
|
||||
false,
|
||||
|
||||
true, ca.disabled);
|
||||
// show edit button, show update button
|
||||
// Don't allow edit if it's running, or else we would lose the "handle" to the ClientApp to stop it.
|
||||
!showStop, false,
|
||||
// show stop button
|
||||
showStop,
|
||||
// show delete button, show start button
|
||||
true, showStart);
|
||||
}
|
||||
|
||||
if ("new".equals(_edit))
|
||||
@@ -258,10 +278,10 @@ public class ConfigClientsHelper extends HelperBase {
|
||||
if (showStartButton && (!ro) && !edit) {
|
||||
buf.append("<button type=\"submit\" class=\"Xaccept\" name=\"action\" value=\"Start ").append(index).append("\" >" + _("Start") + "<span class=hide> ").append(index).append("</span></button>");
|
||||
}
|
||||
if (showEditButton && (!edit) && !ro)
|
||||
buf.append("<button type=\"submit\" class=\"Xadd\" name=\"edit\" value=\"Edit ").append(index).append("\" >" + _("Edit") + "<span class=hide> ").append(index).append("</span></button>");
|
||||
if (showStopButton && (!edit))
|
||||
buf.append("<button type=\"submit\" class=\"Xstop\" name=\"action\" value=\"Stop ").append(index).append("\" >" + _("Stop") + "<span class=hide> ").append(index).append("</span></button>");
|
||||
if (showEditButton && (!edit) && !ro)
|
||||
buf.append("<button type=\"submit\" class=\"Xadd\" name=\"edit\" value=\"Edit ").append(index).append("\" >" + _("Edit") + "<span class=hide> ").append(index).append("</span></button>");
|
||||
if (showUpdateButton && (!edit) && !ro) {
|
||||
buf.append("<button type=\"submit\" class=\"Xcheck\" name=\"action\" value=\"Check ").append(index).append("\" >" + _("Check for updates") + "<span class=hide> ").append(index).append("</span></button>");
|
||||
buf.append("<button type=\"submit\" class=\"Xdownload\" name=\"action\" value=\"Update ").append(index).append("\" >" + _("Update") + "<span class=hide> ").append(index).append("</span></button>");
|
||||
|
||||
@@ -12,7 +12,7 @@ import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.util.RouterPasswordManager;
|
||||
|
||||
//import org.mortbay.jetty.security.UnixCrypt;
|
||||
//import org.eclipse.jetty.util.security.UnixCrypt;
|
||||
|
||||
/**
|
||||
* Manage both plaintext and salted/hashed password storage in
|
||||
|
||||
@@ -126,6 +126,7 @@ public class GraphHelper extends FormHandler {
|
||||
|
||||
/**
|
||||
* For single stat page
|
||||
* @param "bw.combined" treated specially
|
||||
* @since 0.9
|
||||
*/
|
||||
public void setStat(String stat) {
|
||||
@@ -151,10 +152,10 @@ public class GraphHelper extends FormHandler {
|
||||
}
|
||||
|
||||
if (hasTx && hasRx && !_showEvents) {
|
||||
_out.write("<a href=\"viewstat?stat=bw.combined"
|
||||
+ "&periodCount=" + (3 * _periodCount )
|
||||
+ "&width=" + (3 * _width)
|
||||
+ "&height=" + (3 * _height)
|
||||
_out.write("<a href=\"graph?stat=bw.combined"
|
||||
+ "&c=" + (3 * _periodCount )
|
||||
+ "&w=" + (3 * _width)
|
||||
+ "&h=" + (3 * _height)
|
||||
+ "\">");
|
||||
String title = _("Combined bandwidth graph");
|
||||
_out.write("<img class=\"statimage\""
|
||||
@@ -200,7 +201,9 @@ public class GraphHelper extends FormHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* For single stat page
|
||||
* For single stat page;
|
||||
* stat = "bw.combined" treated specially
|
||||
*
|
||||
* @since 0.9
|
||||
*/
|
||||
public String getSingleStat() {
|
||||
@@ -208,25 +211,36 @@ public class GraphHelper extends FormHandler {
|
||||
if (StatSummarizer.isDisabled())
|
||||
return "";
|
||||
if (_stat == null) {
|
||||
_out.write("No stat");
|
||||
_out.write("No stat specified");
|
||||
return "";
|
||||
}
|
||||
List<Rate> rates = StatSummarizer.instance().parseSpecs(_stat);
|
||||
if (rates.size() != 1) {
|
||||
_out.write("Graphs not enabled for " + _stat);
|
||||
return "";
|
||||
long period;
|
||||
String name, displayName;
|
||||
if (_stat.equals("bw.combined")) {
|
||||
period = 60000;
|
||||
name = _stat;
|
||||
displayName = _("Bandwidth usage");
|
||||
} else {
|
||||
List<Rate> rates = StatSummarizer.instance().parseSpecs(_stat);
|
||||
if (rates.size() != 1) {
|
||||
_out.write("Graphs not enabled for " + _stat);
|
||||
return "";
|
||||
}
|
||||
Rate r = rates.get(0);
|
||||
period = r.getPeriod();
|
||||
name = r.getRateStat().getName();
|
||||
displayName = name;
|
||||
}
|
||||
Rate r = rates.get(0);
|
||||
_out.write("<h3>");
|
||||
_out.write(_("{0} for {1}", r.getRateStat().getName(), DataHelper.formatDuration2(_periodCount * r.getPeriod())));
|
||||
_out.write(_("{0} for {1}", displayName, DataHelper.formatDuration2(_periodCount * period)));
|
||||
if (_end > 0)
|
||||
_out.write(' ' + _("ending {0} ago", DataHelper.formatDuration2(_end * r.getPeriod())));
|
||||
_out.write(' ' + _("ending {0} ago", DataHelper.formatDuration2(_end * period)));
|
||||
|
||||
_out.write("</h3><img class=\"statimage\" border=\"0\""
|
||||
+ " src=\"viewstat.jsp?stat="
|
||||
+ r.getRateStat().getName()
|
||||
+ name
|
||||
+ "&showEvents=" + _showEvents
|
||||
+ "&period=" + r.getPeriod()
|
||||
+ "&period=" + period
|
||||
+ "&periodCount=" + _periodCount
|
||||
+ "&end=" + _end
|
||||
+ "&width=" + _width
|
||||
@@ -302,7 +316,8 @@ public class GraphHelper extends FormHandler {
|
||||
|
||||
_out.write("<br>");
|
||||
_out.write(link(_stat, !_showEvents, _periodCount, _end, _width, _height));
|
||||
_out.write(_showEvents ? _("Plot averages") : _("plot events"));
|
||||
if (!_stat.equals("bw.combined"))
|
||||
_out.write(_showEvents ? _("Plot averages") : _("plot events"));
|
||||
_out.write("</a>");
|
||||
|
||||
_out.write("</p><p><i>" + _("All times are UTC.") + "</i></p>\n");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -10,7 +11,11 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
import org.mortbay.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import org.eclipse.jetty.server.session.SessionHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
|
||||
/**
|
||||
* Convert foo.jsp to foo_xx.jsp for language xx.
|
||||
@@ -21,14 +26,22 @@ import org.mortbay.jetty.webapp.WebAppContext;
|
||||
*
|
||||
* @author zzz
|
||||
*/
|
||||
public class LocaleWebAppHandler extends WebAppContext
|
||||
public class LocaleWebAppHandler extends HandlerWrapper
|
||||
{
|
||||
private final I2PAppContext _context;
|
||||
private final WebAppContext _wac;
|
||||
|
||||
public LocaleWebAppHandler(I2PAppContext ctx, String path, String warPath) {
|
||||
super(warPath, path);
|
||||
public LocaleWebAppHandler(I2PAppContext ctx, String path, String warPath,
|
||||
File tmpdir, ServletHandler servletHandler) {
|
||||
super();
|
||||
_context = ctx;
|
||||
_wac = new WebAppContext(warPath, path);
|
||||
setInitParams(WebAppStarter.INIT_PARAMS);
|
||||
_wac.setTempDirectory(tmpdir);
|
||||
_wac.setExtractWAR(false);
|
||||
_wac.setSessionHandler(new SessionHandler());
|
||||
_wac.setServletHandler(servletHandler);
|
||||
setHandler(_wac);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,19 +50,12 @@ public class LocaleWebAppHandler extends WebAppContext
|
||||
* or as specified in the routerconsole.lang property.
|
||||
* Unless language == "en".
|
||||
*/
|
||||
@Override
|
||||
public void handle(String pathInContext,
|
||||
Request baseRequest,
|
||||
HttpServletRequest httpRequest,
|
||||
HttpServletResponse httpResponse,
|
||||
int dispatch)
|
||||
HttpServletResponse httpResponse)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
// Handle OPTIONS (nothing to override)
|
||||
if ("OPTIONS".equals(httpRequest.getMethod()))
|
||||
{
|
||||
handleOptions(httpRequest, httpResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
// transparent rewriting
|
||||
if (pathInContext.equals("/") || pathInContext.equals("/index.html")) {
|
||||
@@ -77,7 +83,7 @@ public class LocaleWebAppHandler extends WebAppContext
|
||||
if (lang != null && lang.length() > 0 && !lang.equals("en")) {
|
||||
String testPath = pathInContext.substring(0, len - 4) + '_' + lang + ".jsp";
|
||||
// Do we have a servlet for the new path that isn't the catchall *.jsp?
|
||||
Map.Entry servlet = getServletHandler().getHolderEntry(testPath);
|
||||
Map.Entry servlet = _wac.getServletHandler().getHolderEntry(testPath);
|
||||
if (servlet != null) {
|
||||
String servletPath = (String) servlet.getKey();
|
||||
if (servletPath != null && !servletPath.startsWith("*")) {
|
||||
@@ -90,7 +96,7 @@ public class LocaleWebAppHandler extends WebAppContext
|
||||
}
|
||||
}
|
||||
//System.err.println("New path: " + newPath);
|
||||
super.handle(newPath, httpRequest, httpResponse, dispatch);
|
||||
super.handle(newPath, baseRequest, httpRequest, httpResponse);
|
||||
//System.err.println("Was handled? " + httpRequest.isHandled());
|
||||
}
|
||||
|
||||
@@ -112,10 +118,28 @@ public class LocaleWebAppHandler extends WebAppContext
|
||||
* Not an override
|
||||
* @since 0.8
|
||||
*/
|
||||
/**** not in Jetty 7
|
||||
public void handleOptions(HttpServletRequest request,
|
||||
HttpServletResponse response)
|
||||
throws IOException
|
||||
{
|
||||
response.sendError(405);
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* Mysteriously removed from Jetty 7
|
||||
*/
|
||||
private void setInitParams(Map params) {
|
||||
setInitParams(_wac, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since Jetty 7
|
||||
*/
|
||||
public static void setInitParams(WebAppContext context, Map<?,?> params) {
|
||||
for (Map.Entry e : params.entrySet()) {
|
||||
context.setInitParameter((String)e.getKey(), (String)e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.VersionComparator;
|
||||
|
||||
import org.mortbay.jetty.Server;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.tanukisoftware.wrapper.WrapperManager;
|
||||
|
||||
public class LogsHelper extends HelperBase {
|
||||
|
||||
@@ -34,7 +34,7 @@ import net.i2p.util.Log;
|
||||
import net.i2p.util.Translate;
|
||||
import net.i2p.util.VersionComparator;
|
||||
|
||||
import org.mortbay.jetty.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
|
||||
|
||||
/**
|
||||
@@ -69,9 +69,13 @@ public class PluginStarter implements Runnable {
|
||||
|
||||
public void run() {
|
||||
if (_context.getBooleanPropertyDefaultTrue("plugins.autoUpdate") &&
|
||||
(!NewsHelper.isUpdateInProgress()) &&
|
||||
(!RouterVersion.VERSION.equals(_context.getProperty("router.previousVersion"))))
|
||||
updateAll(_context, true);
|
||||
!NewsHelper.isUpdateInProgress()) {
|
||||
String prev = _context.getProperty("router.previousVersion");
|
||||
if (prev != null &&
|
||||
(new VersionComparator()).compare(RouterVersion.VERSION, prev) > 0) {
|
||||
updateAll(_context, true);
|
||||
}
|
||||
}
|
||||
startPlugins(_context);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import static net.i2p.app.ClientAppState.*;
|
||||
import net.i2p.apps.systray.SysTray;
|
||||
import net.i2p.data.Base32;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.jetty.I2PLogger;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.update.ConsoleUpdateManager;
|
||||
import net.i2p.router.app.RouterApp;
|
||||
@@ -42,35 +43,55 @@ import net.i2p.util.ShellCommand;
|
||||
import net.i2p.util.SystemVersion;
|
||||
import net.i2p.util.VersionComparator;
|
||||
|
||||
import org.mortbay.jetty.AbstractConnector;
|
||||
import org.mortbay.jetty.Connector;
|
||||
import org.mortbay.jetty.NCSARequestLog;
|
||||
import org.mortbay.jetty.Server;
|
||||
import org.mortbay.jetty.bio.SocketConnector;
|
||||
import org.mortbay.jetty.handler.ContextHandlerCollection;
|
||||
import org.mortbay.jetty.handler.DefaultHandler;
|
||||
import org.mortbay.jetty.handler.HandlerCollection;
|
||||
import org.mortbay.jetty.handler.RequestLogHandler;
|
||||
import org.mortbay.jetty.nio.SelectChannelConnector;
|
||||
import org.mortbay.jetty.security.Credential.MD5;
|
||||
import org.mortbay.jetty.security.DigestAuthenticator;
|
||||
import org.mortbay.jetty.security.HashUserRealm;
|
||||
import org.mortbay.jetty.security.Constraint;
|
||||
import org.mortbay.jetty.security.ConstraintMapping;
|
||||
import org.mortbay.jetty.security.SecurityHandler;
|
||||
import org.mortbay.jetty.security.SslSocketConnector;
|
||||
import org.mortbay.jetty.security.SslSelectChannelConnector;
|
||||
import org.mortbay.jetty.servlet.ServletHandler;
|
||||
import org.mortbay.jetty.servlet.ServletHolder;
|
||||
import org.mortbay.jetty.servlet.SessionHandler;
|
||||
import org.mortbay.jetty.webapp.WebAppContext;
|
||||
import org.mortbay.thread.QueuedThreadPool;
|
||||
import org.mortbay.thread.concurrent.ThreadPool;
|
||||
import org.eclipse.jetty.security.HashLoginService;
|
||||
import org.eclipse.jetty.security.ConstraintMapping;
|
||||
import org.eclipse.jetty.security.ConstraintSecurityHandler;
|
||||
import org.eclipse.jetty.security.SecurityHandler;
|
||||
import org.eclipse.jetty.security.authentication.DigestAuthenticator;
|
||||
import org.eclipse.jetty.server.AbstractConnector;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.NCSARequestLog;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.bio.SocketConnector;
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import org.eclipse.jetty.server.handler.RequestLogHandler;
|
||||
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
||||
import org.eclipse.jetty.server.ssl.SslSocketConnector;
|
||||
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.security.Constraint;
|
||||
import org.eclipse.jetty.util.security.Credential;
|
||||
import org.eclipse.jetty.util.security.Credential.MD5;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.ExecutorThreadPool;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.eclipse.jetty.util.thread.ThreadPool;
|
||||
|
||||
/**
|
||||
* Start the router console.
|
||||
*/
|
||||
public class RouterConsoleRunner implements RouterApp {
|
||||
|
||||
static {
|
||||
// To take effect, must be set before any Jetty classes are loaded
|
||||
try {
|
||||
Log.setLog(new I2PLogger());
|
||||
} catch (Throwable t) {
|
||||
System.err.println("INFO: I2P Jetty logging class not found, logging to wrapper log");
|
||||
}
|
||||
// This way it doesn't try to load Slf4jLog first
|
||||
// This causes an NPE in AbstractLifeCycle
|
||||
// http://dev.eclipse.org/mhonarc/lists/jetty-users/msg02587.html
|
||||
//System.setProperty("org.eclipse.jetty.util.log.class", "net.i2p.jetty.I2PLogger");
|
||||
}
|
||||
|
||||
private final RouterContext _context;
|
||||
private final ClientAppManager _mgr;
|
||||
private volatile ClientAppState _state = UNINITIALIZED;
|
||||
@@ -253,7 +274,9 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
} else {
|
||||
// required true for jrobin to work
|
||||
System.setProperty("java.awt.headless", "true");
|
||||
SysTray.getInstance();
|
||||
// this check is in SysTray but do it here too
|
||||
if (SystemVersion.isWindows() && (!Boolean.getBoolean("systray.disable")) && (!SystemVersion.is64Bit()))
|
||||
SysTray.getInstance();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
@@ -286,14 +309,6 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
boolean workDirCreated = workDir.mkdirs();
|
||||
if (!workDirCreated)
|
||||
System.err.println("ERROR: Unable to create Jetty temporary work directory");
|
||||
|
||||
//try {
|
||||
// Log.setLog(new I2PLogger(_context));
|
||||
//} catch (Throwable t) {
|
||||
// System.err.println("INFO: I2P Jetty logging class not found, logging to wrapper log");
|
||||
//}
|
||||
// This way it doesn't try to load Slf4jLog first
|
||||
System.setProperty("org.mortbay.log.class", "net.i2p.jetty.I2PLogger");
|
||||
|
||||
// so Jetty can find WebAppConfiguration
|
||||
System.setProperty("jetty.class.path", _context.getBaseDir() + "/lib/routerconsole.jar");
|
||||
@@ -302,7 +317,8 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
|
||||
try {
|
||||
ThreadPool ctp = new CustomThreadPoolExecutor();
|
||||
ctp.prestartAllCoreThreads();
|
||||
// Gone in Jetty 7
|
||||
//ctp.prestartAllCoreThreads();
|
||||
_server.setThreadPool(ctp);
|
||||
} catch (Throwable t) {
|
||||
// class not found...
|
||||
@@ -315,7 +331,9 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
|
||||
HandlerCollection hColl = new HandlerCollection();
|
||||
ContextHandlerCollection chColl = new ContextHandlerCollection();
|
||||
_server.addHandler(hColl);
|
||||
// gone in Jetty 7
|
||||
//_server.addHandler(hColl);
|
||||
_server.setHandler(hColl);
|
||||
hColl.addHandler(chColl);
|
||||
hColl.addHandler(new DefaultHandler());
|
||||
|
||||
@@ -351,7 +369,7 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
if (!_webAppsDir.endsWith("/"))
|
||||
_webAppsDir += '/';
|
||||
|
||||
WebAppContext rootWebApp = null;
|
||||
HandlerWrapper rootWebApp = null;
|
||||
ServletHandler rootServletHandler = null;
|
||||
List<Connector> connectors = new ArrayList(4);
|
||||
try {
|
||||
@@ -438,6 +456,11 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
if (sslPort > 0) {
|
||||
File keyStore = new File(_context.getConfigDir(), "keystore/console.ks");
|
||||
if (verifyKeyStore(keyStore)) {
|
||||
// the keystore path and password
|
||||
SslContextFactory sslFactory = new SslContextFactory(keyStore.getAbsolutePath());
|
||||
sslFactory.setKeyStorePassword(_context.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD));
|
||||
// the X.509 cert password (if not present, verifyKeyStore() returned false)
|
||||
sslFactory.setKeyManagerPassword(_context.getProperty(PROP_KEY_PASSWORD, "thisWontWork"));
|
||||
StringTokenizer tok = new StringTokenizer(_sslListenHost, " ,");
|
||||
while (tok.hasMoreTokens()) {
|
||||
String host = tok.nextToken().trim();
|
||||
@@ -461,25 +484,14 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
if (testSock != null) try { testSock.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
// TODO if class not found use SslChannelConnector
|
||||
// Sadly there's no common base class with the ssl methods in it
|
||||
AbstractConnector ssll;
|
||||
if (SystemVersion.isJava6() && !SystemVersion.isGNU()) {
|
||||
SslSelectChannelConnector sssll = new SslSelectChannelConnector();
|
||||
// the keystore path and password
|
||||
sssll.setKeystore(keyStore.getAbsolutePath());
|
||||
sssll.setPassword(_context.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD));
|
||||
// the X.509 cert password (if not present, verifyKeyStore() returned false)
|
||||
sssll.setKeyPassword(_context.getProperty(PROP_KEY_PASSWORD, "thisWontWork"));
|
||||
SslSelectChannelConnector sssll = new SslSelectChannelConnector(sslFactory);
|
||||
sssll.setUseDirectBuffers(false); // default true seems to be leaky
|
||||
ssll = sssll;
|
||||
} else {
|
||||
// Jetty 6 and NIO on Java 5 don't get along that well
|
||||
SslSocketConnector sssll = new SslSocketConnector();
|
||||
// the keystore path and password
|
||||
sssll.setKeystore(keyStore.getAbsolutePath());
|
||||
sssll.setPassword(_context.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD));
|
||||
// the X.509 cert password (if not present, verifyKeyStore() returned false)
|
||||
sssll.setKeyPassword(_context.getProperty(PROP_KEY_PASSWORD, "thisWontWork"));
|
||||
SslSocketConnector sssll = new SslSocketConnector(sslFactory);
|
||||
ssll = sssll;
|
||||
}
|
||||
ssll.setHost(host);
|
||||
@@ -507,23 +519,24 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
return;
|
||||
}
|
||||
|
||||
rootWebApp = new LocaleWebAppHandler(_context,
|
||||
"/", _webAppsDir + ROUTERCONSOLE + ".war");
|
||||
File tmpdir = new SecureDirectory(workDir, ROUTERCONSOLE + "-" +
|
||||
(_listenPort != null ? _listenPort : _sslListenPort));
|
||||
tmpdir.mkdir();
|
||||
rootWebApp.setTempDirectory(tmpdir);
|
||||
rootWebApp.setExtractWAR(false);
|
||||
rootWebApp.setSessionHandler(new SessionHandler());
|
||||
rootServletHandler = new ServletHandler();
|
||||
rootWebApp.setServletHandler(rootServletHandler);
|
||||
initialize(_context, rootWebApp);
|
||||
rootWebApp = new LocaleWebAppHandler(_context,
|
||||
"/", _webAppsDir + ROUTERCONSOLE + ".war",
|
||||
tmpdir, rootServletHandler);
|
||||
initialize(_context, (WebAppContext)(rootWebApp.getHandler()));
|
||||
chColl.addHandler(rootWebApp);
|
||||
|
||||
} catch (Exception ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
|
||||
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=364936
|
||||
// WARN:oejw.WebAppContext:Failed startup of context o.e.j.w.WebAppContext{/,jar:file:/.../webapps/routerconsole.war!/},/.../webapps/routerconsole.war
|
||||
// java.lang.IllegalStateException: zip file closed
|
||||
Resource.setDefaultUseCaches(false);
|
||||
try {
|
||||
// start does a mapContexts()
|
||||
_server.start();
|
||||
@@ -592,6 +605,8 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
}
|
||||
}
|
||||
changeState(RUNNING);
|
||||
if (_mgr != null)
|
||||
_mgr.register(this);
|
||||
}
|
||||
} else {
|
||||
System.err.println("ERROR: Router console did not start, not starting webapps");
|
||||
@@ -727,7 +742,7 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
* Add all users and passwords.
|
||||
*/
|
||||
static void initialize(RouterContext ctx, WebAppContext context) {
|
||||
SecurityHandler sec = new SecurityHandler();
|
||||
ConstraintSecurityHandler sec = new ConstraintSecurityHandler();
|
||||
List<ConstraintMapping> constraints = new ArrayList(4);
|
||||
ConsolePasswordManager mgr = new ConsolePasswordManager(ctx);
|
||||
boolean enable = ctx.getBooleanProperty(PROP_PW_ENABLE);
|
||||
@@ -737,14 +752,13 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
enable = false;
|
||||
ctx.router().saveConfig(PROP_CONSOLE_PW, "false");
|
||||
} else {
|
||||
HashUserRealm realm = new HashUserRealm(JETTY_REALM);
|
||||
sec.setUserRealm(realm);
|
||||
HashLoginService realm = new HashLoginService(JETTY_REALM);
|
||||
sec.setLoginService(realm);
|
||||
sec.setAuthenticator(authenticator);
|
||||
for (Map.Entry<String, String> e : userpw.entrySet()) {
|
||||
String user = e.getKey();
|
||||
String pw = e.getValue();
|
||||
realm.put(user, MD5.__TYPE + pw);
|
||||
realm.addUserToRole(user, JETTY_ROLE);
|
||||
realm.putUser(user, Credential.getCredential(MD5.__TYPE + pw), new String[] {JETTY_ROLE});
|
||||
Constraint constraint = new Constraint(user, JETTY_ROLE);
|
||||
constraint.setAuthenticate(true);
|
||||
ConstraintMapping cm = new ConstraintMapping();
|
||||
@@ -843,11 +857,13 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
* Just to set the name and set Daemon
|
||||
* @since Jetty 6
|
||||
*/
|
||||
private static class CustomThreadPoolExecutor extends ThreadPool {
|
||||
private static class CustomThreadPoolExecutor extends ExecutorThreadPool {
|
||||
public CustomThreadPoolExecutor() {
|
||||
super(MIN_THREADS, MAX_THREADS, MAX_IDLE_TIME, TimeUnit.MILLISECONDS,
|
||||
new SynchronousQueue(), new CustomThreadFactory(),
|
||||
new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
new SynchronousQueue() /** , following args not available in Jetty 7
|
||||
new CustomThreadFactory(),
|
||||
new ThreadPoolExecutor.CallerRunsPolicy() **/
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,14 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import javax.imageio.stream.MemoryCacheImageOutputStream;
|
||||
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.stat.Rate;
|
||||
import net.i2p.stat.RateStat;
|
||||
@@ -25,18 +16,13 @@ import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
import org.jrobin.graph.RrdGraph;
|
||||
import org.jrobin.graph.RrdGraphDef;
|
||||
|
||||
/**
|
||||
* A thread started by RouterConsoleRunner that
|
||||
* checks the configuration for stats to be tracked via jrobin,
|
||||
* and adds or deletes RRDs as necessary.
|
||||
*
|
||||
* This also contains methods to generate xml or png image output.
|
||||
* The actual png rendering code is here for the special dual-rate graph;
|
||||
* the rendering for standard graphs is in SummaryRenderer.
|
||||
* The rendering for graphs is in SummaryRenderer.
|
||||
*
|
||||
* To control memory, the number of simultaneous renderings is limited.
|
||||
*
|
||||
@@ -47,6 +33,7 @@ public class StatSummarizer implements Runnable {
|
||||
private final Log _log;
|
||||
/** list of SummaryListener instances */
|
||||
private final List<SummaryListener> _listeners;
|
||||
// TODO remove static instance
|
||||
private static StatSummarizer _instance;
|
||||
private static final int MAX_CONCURRENT_PNG = 3;
|
||||
private final Semaphore _sem;
|
||||
@@ -93,6 +80,18 @@ public class StatSummarizer implements Runnable {
|
||||
static boolean isDisabled() {
|
||||
return _instance == null || _instance._isDisabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable graph generation until restart
|
||||
* See SummaryRenderer.render()
|
||||
* @since 0.9.6
|
||||
*/
|
||||
static void setDisabled() {
|
||||
if (_instance != null) {
|
||||
_instance._isDisabled = true;
|
||||
_instance._isRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** list of SummaryListener instances */
|
||||
List<SummaryListener> getListeners() { return _listeners; }
|
||||
@@ -245,7 +244,7 @@ public class StatSummarizer implements Runnable {
|
||||
if (lsnr.getRate().equals(rate)) {
|
||||
lsnr.getData().exportXml(out);
|
||||
out.write(("<!-- Rate: " + lsnr.getRate().getRateStat().getName() + " for period " + lsnr.getRate().getPeriod() + " -->\n").getBytes());
|
||||
out.write(("<!-- Average data soure name: " + lsnr.getName() + " event count data source name: " + lsnr.getEventName() + " -->\n").getBytes());
|
||||
out.write(("<!-- Average data source name: " + lsnr.getName() + " event count data source name: " + lsnr.getEventName() + " -->\n").getBytes());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -254,19 +253,21 @@ public class StatSummarizer implements Runnable {
|
||||
|
||||
/**
|
||||
* This does the two-data bandwidth graph only.
|
||||
* For all other graphs see SummaryRenderer
|
||||
* For all other graphs see renderPng() above.
|
||||
* Synchronized to conserve memory.
|
||||
*
|
||||
* @param end number of periods before now
|
||||
* @return success
|
||||
*/
|
||||
public boolean renderRatePng(OutputStream out, int width, int height, boolean hideLegend,
|
||||
boolean hideGrid, boolean hideTitle, boolean showEvents,
|
||||
int periodCount, boolean showCredit) throws IOException {
|
||||
int periodCount, int end, boolean showCredit) throws IOException {
|
||||
try {
|
||||
try {
|
||||
_sem.acquire();
|
||||
} catch (InterruptedException ie) {}
|
||||
return locked_renderRatePng(out, width, height, hideLegend, hideGrid, hideTitle, showEvents,
|
||||
periodCount, showCredit);
|
||||
periodCount, end, showCredit);
|
||||
} finally {
|
||||
_sem.release();
|
||||
}
|
||||
@@ -274,7 +275,7 @@ public class StatSummarizer implements Runnable {
|
||||
|
||||
private boolean locked_renderRatePng(OutputStream out, int width, int height, boolean hideLegend,
|
||||
boolean hideGrid, boolean hideTitle, boolean showEvents,
|
||||
int periodCount, boolean showCredit) throws IOException {
|
||||
int periodCount, int end, boolean showCredit) throws IOException {
|
||||
|
||||
// go to some trouble to see if we have the data for the combined bw graph
|
||||
SummaryListener txLsnr = null;
|
||||
@@ -289,7 +290,6 @@ public class StatSummarizer implements Runnable {
|
||||
if (txLsnr == null || rxLsnr == null)
|
||||
throw new IOException("no rates for combined graph");
|
||||
|
||||
long end = _context.clock().now() - 75*1000;
|
||||
if (width > GraphHelper.MAX_X)
|
||||
width = GraphHelper.MAX_X;
|
||||
else if (width <= 0)
|
||||
@@ -298,86 +298,9 @@ public class StatSummarizer implements Runnable {
|
||||
height = GraphHelper.MAX_Y;
|
||||
else if (height <= 0)
|
||||
height = GraphHelper.DEFAULT_Y;
|
||||
if (periodCount <= 0 || periodCount > txLsnr.getRows())
|
||||
periodCount = txLsnr.getRows();
|
||||
long period = 60*1000;
|
||||
long start = end - period*periodCount;
|
||||
//long begin = System.currentTimeMillis();
|
||||
ImageOutputStream ios = null;
|
||||
try {
|
||||
RrdGraphDef def = new RrdGraphDef();
|
||||
def.setTimeSpan(start/1000, end/1000);
|
||||
def.setMinValue(0d);
|
||||
def.setBase(1024);
|
||||
String title = _("Bandwidth usage");
|
||||
if (!hideTitle)
|
||||
def.setTitle(title);
|
||||
long started = _context.router().getWhenStarted();
|
||||
if (started > start && started < end)
|
||||
def.vrule(started / 1000, SummaryRenderer.RESTART_BAR_COLOR, null, 4.0f); // no room for legend
|
||||
String sendName = SummaryListener.createName(_context, "bw.sendRate.60000");
|
||||
String recvName = SummaryListener.createName(_context, "bw.recvRate.60000");
|
||||
def.datasource(sendName, txLsnr.getData().getPath(), sendName, SummaryListener.CF, txLsnr.getBackendName());
|
||||
def.datasource(recvName, rxLsnr.getData().getPath(), recvName, SummaryListener.CF, rxLsnr.getBackendName());
|
||||
def.area(sendName, Color.BLUE, _("Outbound Bytes/sec"));
|
||||
//def.line(sendName, Color.BLUE, "Outbound bytes/sec", 3);
|
||||
def.line(recvName, Color.RED, _("Inbound Bytes/sec") + "\\r", 3);
|
||||
//def.area(recvName, Color.RED, "Inbound bytes/sec@r");
|
||||
if (!hideLegend) {
|
||||
def.gprint(sendName, SummaryListener.CF, _("Out average") + ": %.2f %s" + _("Bps"));
|
||||
def.gprint(sendName, "MAX", ' ' + _("max") + ": %.2f %S" + _("Bps") + "\\r");
|
||||
def.gprint(recvName, SummaryListener.CF, _("In average") + ": %.2f %S" + _("Bps"));
|
||||
def.gprint(recvName, "MAX", ' ' + _("max") + ": %.2f %S" + _("Bps") + "\\r");
|
||||
// '07-Jul 21:09 UTC' with month name in the system locale
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM HH:mm");
|
||||
def.comment(sdf.format(new Date(start)) + " -- " + sdf.format(new Date(end)) + " UTC\\r");
|
||||
}
|
||||
if (!showCredit)
|
||||
def.setShowSignature(false);
|
||||
if (hideLegend)
|
||||
def.setNoLegend(true);
|
||||
if (hideGrid) {
|
||||
def.setDrawXGrid(false);
|
||||
def.setDrawYGrid(false);
|
||||
}
|
||||
//System.out.println("rendering: path=" + path + " dsNames[0]=" + dsNames[0] + " dsNames[1]=" + dsNames[1] + " lsnr.getName=" + _listener.getName());
|
||||
def.setAntiAliasing(false);
|
||||
//System.out.println("Rendering: \n" + def.exportXmlTemplate());
|
||||
//System.out.println("*****************\nData: \n" + _listener.getData().dump());
|
||||
def.setWidth(width);
|
||||
def.setHeight(height);
|
||||
def.setImageFormat("PNG");
|
||||
def.setLazy(true);
|
||||
|
||||
RrdGraph graph = new RrdGraph(def);
|
||||
//System.out.println("Graph created");
|
||||
int totalWidth = graph.getRrdGraphInfo().getWidth();
|
||||
int totalHeight = graph.getRrdGraphInfo().getHeight();
|
||||
BufferedImage img = new BufferedImage(totalWidth, totalHeight, BufferedImage.TYPE_USHORT_565_RGB);
|
||||
Graphics gfx = img.getGraphics();
|
||||
graph.render(gfx);
|
||||
ios = new MemoryCacheImageOutputStream(out);
|
||||
ImageIO.write(img, "png", ios);
|
||||
|
||||
//File t = File.createTempFile("jrobinData", ".xml");
|
||||
//_listener.getData().dumpXml(new FileOutputStream(t));
|
||||
//System.out.println("plotted: " + (data != null ? data.length : 0) + " bytes in " + timeToPlot
|
||||
// ); // + ", data written to " + t.getAbsolutePath());
|
||||
return true;
|
||||
} catch (RrdException re) {
|
||||
_log.error("Error rendering", re);
|
||||
throw new IOException("Error plotting: " + re.getMessage());
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error rendering", ioe);
|
||||
throw ioe;
|
||||
} catch (OutOfMemoryError oom) {
|
||||
_log.error("Error rendering", oom);
|
||||
throw new IOException("Error plotting: " + oom.getMessage());
|
||||
} finally {
|
||||
// this does not close the underlying stream
|
||||
if (ios != null) try {ios.close();} catch (IOException ioe) {}
|
||||
}
|
||||
txLsnr.renderPng(out, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount,
|
||||
end, showCredit, rxLsnr, _("Bandwidth usage"));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -187,14 +187,31 @@ class SummaryListener implements RateSummaryListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Single graph.
|
||||
*
|
||||
* @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 {
|
||||
renderPng(out, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount,
|
||||
end, showCredit, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Single or two-data-source graph.
|
||||
*
|
||||
* @param lsnr2 2nd data source to plot on same graph, or null. Not recommended for events.
|
||||
* @param titleOverride If non-null, overrides the title
|
||||
* @since 0.9.6
|
||||
*/
|
||||
public void renderPng(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid,
|
||||
boolean hideTitle, boolean showEvents, int periodCount,
|
||||
int end, boolean showCredit, SummaryListener lsnr2, String titleOverride) throws IOException {
|
||||
if (_renderer == null || _db == null)
|
||||
throw new IOException("No RRD, check logs for previous errors");
|
||||
_renderer.render(out, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount, end, showCredit);
|
||||
_renderer.render(out, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount,
|
||||
end, showCredit, lsnr2, titleOverride);
|
||||
}
|
||||
|
||||
public void renderPng(OutputStream out) throws IOException {
|
||||
|
||||
@@ -27,8 +27,7 @@ import org.jrobin.graph.RrdGraphDefTemplate;
|
||||
|
||||
/**
|
||||
* Generate the RRD graph png images,
|
||||
* except for the combined rate graph, which is
|
||||
* generated in StatSummarizer.
|
||||
* including the combined rate graph.
|
||||
*
|
||||
* @since 0.6.1.13
|
||||
*/
|
||||
@@ -36,7 +35,7 @@ class SummaryRenderer {
|
||||
private final Log _log;
|
||||
private final SummaryListener _listener;
|
||||
private final I2PAppContext _context;
|
||||
static final Color RESTART_BAR_COLOR = new Color(255, 144, 0, 224);
|
||||
private static final Color RESTART_BAR_COLOR = new Color(255, 144, 0, 224);
|
||||
|
||||
public SummaryRenderer(I2PAppContext ctx, SummaryListener lsnr) {
|
||||
_log = ctx.logManager().getLog(SummaryRenderer.class);
|
||||
@@ -51,8 +50,11 @@ class SummaryRenderer {
|
||||
* specify who can get it from where, etc.
|
||||
*
|
||||
* @deprecated unsed
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
public static synchronized void render(I2PAppContext ctx, OutputStream out, String filename) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
/*****
|
||||
long end = ctx.clock().now() - 60*1000;
|
||||
long start = end - 60*1000*SummaryListener.PERIODS;
|
||||
try {
|
||||
@@ -80,17 +82,34 @@ class SummaryRenderer {
|
||||
//_log.error("Error rendering " + filename, ioe);
|
||||
throw ioe;
|
||||
}
|
||||
*****/
|
||||
}
|
||||
|
||||
public void render(OutputStream out) throws IOException { render(out, GraphHelper.DEFAULT_X, GraphHelper.DEFAULT_Y,
|
||||
false, false, false, false, -1, 0, false); }
|
||||
|
||||
/**
|
||||
* Single graph.
|
||||
*
|
||||
* @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 {
|
||||
render(out, width, height, hideLegend, hideGrid, hideTitle,
|
||||
showEvents, periodCount, endp, showCredit, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Single or two-data-source graph.
|
||||
*
|
||||
* @param lsnr2 2nd data source to plot on same graph, or null. Not recommended for events.
|
||||
* @param titleOverride If non-null, overrides the title
|
||||
* @since 0.9.6 consolidated from StatSummarizer for bw.combined
|
||||
*/
|
||||
public void render(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid,
|
||||
boolean hideTitle, boolean showEvents, int periodCount,
|
||||
int endp, boolean showCredit, SummaryListener lsnr2, String titleOverride) throws IOException {
|
||||
long end = _listener.now() - 75*1000;
|
||||
long period = _listener.getRate().getPeriod();
|
||||
if (endp > 0)
|
||||
@@ -109,7 +128,9 @@ class SummaryRenderer {
|
||||
if ((name.startsWith("bw.") || name.indexOf("Size") >= 0 || name.indexOf("Bps") >= 0 || name.indexOf("memory") >= 0)
|
||||
&& !showEvents)
|
||||
def.setBase(1024);
|
||||
if (!hideTitle) {
|
||||
if (titleOverride != null) {
|
||||
def.setTitle(titleOverride);
|
||||
} else if (!hideTitle) {
|
||||
String title;
|
||||
String p;
|
||||
// we want the formatting and translation of formatDuration2(), except not zh, and not the
|
||||
@@ -146,14 +167,31 @@ class SummaryRenderer {
|
||||
// def.vrule(started / 1000, RESTART_BAR_COLOR, _("Restart"), 4.0f);
|
||||
|
||||
def.datasource(plotName, path, plotName, SummaryListener.CF, _listener.getBackendName());
|
||||
if (descr.length() > 0)
|
||||
if (descr.length() > 0) {
|
||||
def.area(plotName, Color.BLUE, descr + "\\r");
|
||||
else
|
||||
} else {
|
||||
def.area(plotName, Color.BLUE);
|
||||
}
|
||||
if (!hideLegend) {
|
||||
def.gprint(plotName, SummaryListener.CF, _("avg") + ": %.2f %s");
|
||||
def.gprint(plotName, "MAX", ' ' + _("max") + ": %.2f %S");
|
||||
def.gprint(plotName, "LAST", ' ' + _("now") + ": %.2f %S\\r");
|
||||
}
|
||||
String plotName2 = null;
|
||||
if (lsnr2 != null) {
|
||||
String dsNames2[] = lsnr2.getData().getDsNames();
|
||||
plotName2 = dsNames2[0];
|
||||
String path2 = lsnr2.getData().getPath();
|
||||
String descr2 = _(lsnr2.getRate().getRateStat().getDescription());
|
||||
def.datasource(plotName2, path2, plotName2, SummaryListener.CF, lsnr2.getBackendName());
|
||||
def.line(plotName2, Color.RED, descr2 + "\\r", 3);
|
||||
if (!hideLegend) {
|
||||
def.gprint(plotName2, SummaryListener.CF, _("avg") + ": %.2f %s");
|
||||
def.gprint(plotName2, "MAX", ' ' + _("max") + ": %.2f %S");
|
||||
def.gprint(plotName2, "LAST", ' ' + _("now") + ": %.2f %S\\r");
|
||||
}
|
||||
}
|
||||
if (!hideLegend) {
|
||||
// '07-Jul 21:09 UTC' with month name in the system locale
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM HH:mm");
|
||||
Map<Long, String> events = ((RouterContext)_context).router().eventLog().getEvents(EventLog.STARTED, start);
|
||||
@@ -191,7 +229,15 @@ class SummaryRenderer {
|
||||
def.setImageFormat("PNG");
|
||||
def.setLazy(true);
|
||||
|
||||
RrdGraph graph = new RrdGraph(def);
|
||||
RrdGraph graph;
|
||||
try {
|
||||
// NPE here if system is missing fonts - see ticket #915
|
||||
graph = new RrdGraph(def);
|
||||
} catch (NullPointerException npe) {
|
||||
_log.error("Error rendering", npe);
|
||||
StatSummarizer.setDisabled();
|
||||
throw new IOException("Error rendering - disabling graph generation. Missing font? See http://trac.i2p2.i2p/ticket/915");
|
||||
}
|
||||
int totalWidth = graph.getRrdGraphInfo().getWidth();
|
||||
int totalHeight = graph.getRrdGraphInfo().getHeight();
|
||||
BufferedImage img = new BufferedImage(totalWidth, totalHeight, BufferedImage.TYPE_USHORT_565_RGB);
|
||||
|
||||
@@ -3,16 +3,19 @@ package net.i2p.router.web;
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
import org.mortbay.jetty.webapp.Configuration;
|
||||
import org.mortbay.jetty.webapp.WebAppClassLoader;
|
||||
import org.mortbay.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.webapp.Configuration;
|
||||
import org.eclipse.jetty.webapp.WebAppClassLoader;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
|
||||
|
||||
/**
|
||||
@@ -38,28 +41,45 @@ import org.mortbay.jetty.webapp.WebAppContext;
|
||||
* @author zzz
|
||||
*/
|
||||
public class WebAppConfiguration implements Configuration {
|
||||
private WebAppContext _wac;
|
||||
|
||||
private static final String CLASSPATH = ".classpath";
|
||||
|
||||
public void setWebAppContext(WebAppContext context) {
|
||||
_wac = context;
|
||||
}
|
||||
|
||||
public WebAppContext getWebAppContext() {
|
||||
return _wac;
|
||||
}
|
||||
|
||||
/**
|
||||
* This was the interface in Jetty 5, now it's configureClassLoader()
|
||||
* This was the interface in Jetty 5, in Jetty 6 was configureClassLoader(),
|
||||
* now it's configure()
|
||||
*/
|
||||
private void configureClassPath() throws Exception {
|
||||
String ctxPath = _wac.getContextPath();
|
||||
private void configureClassPath(WebAppContext wac) throws Exception {
|
||||
String ctxPath = wac.getContextPath();
|
||||
//System.err.println("Configure Class Path " + ctxPath);
|
||||
if (ctxPath.equals("/"))
|
||||
return;
|
||||
String appName = ctxPath.substring(1);
|
||||
|
||||
if (ctxPath.equals("/susimail")) {
|
||||
// allow certain Jetty classes, restricted as of Jetty 7
|
||||
// See http://wiki.eclipse.org/Jetty/Reference/Jetty_Classloading
|
||||
//System.err.println("Allowing Jetty utils in classpath for " + appName);
|
||||
//System.err.println("System classes before: " + Arrays.toString(wac.getSystemClasses()));
|
||||
//System.err.println("Server classes before: " + Arrays.toString(wac.getServerClasses()));
|
||||
wac.addSystemClass("org.eclipse.jetty.http.");
|
||||
wac.addSystemClass("org.eclipse.jetty.io.");
|
||||
wac.addSystemClass("org.eclipse.jetty.util.");
|
||||
// org.eclipse.jetty.webapp.ClasspathPattern looks in-order, and
|
||||
// WebAppContext doesn't provide a remove method, so we must
|
||||
// convert to a list, remove the wildcard entry, add ours, then
|
||||
// add the wildcard back, then reset.
|
||||
List<String> classes = new ArrayList(16);
|
||||
classes.addAll(Arrays.asList(wac.getServerClasses()));
|
||||
classes.remove("org.eclipse.jetty.");
|
||||
classes.add("-org.eclipse.jetty.http.");
|
||||
classes.add("-org.eclipse.jetty.io.");
|
||||
classes.add("-org.eclipse.jetty.util.");
|
||||
classes.add("org.eclipse.jetty.");
|
||||
wac.setServerClasses(classes.toArray(new String[classes.size()]));
|
||||
//System.err.println("System classes after: " + Arrays.toString(wac.getSystemClasses()));
|
||||
//System.err.println("Server classes after: " + Arrays.toString(wac.getServerClasses()));
|
||||
}
|
||||
|
||||
I2PAppContext i2pContext = I2PAppContext.getGlobalContext();
|
||||
File libDir = new File(i2pContext.getBaseDir(), "lib");
|
||||
// FIXME this only works if war is the same name as the plugin
|
||||
@@ -110,7 +130,7 @@ public class WebAppConfiguration implements Configuration {
|
||||
}
|
||||
if (buf.length() <= 0)
|
||||
return;
|
||||
ClassLoader cl = _wac.getClassLoader();
|
||||
ClassLoader cl = wac.getClassLoader();
|
||||
if (cl != null && cl instanceof WebAppClassLoader) {
|
||||
WebAppClassLoader wacl = (WebAppClassLoader) cl;
|
||||
wacl.addClassPath(buf.toString());
|
||||
@@ -118,7 +138,7 @@ public class WebAppConfiguration implements Configuration {
|
||||
// This was not working because the WebAppClassLoader already exists
|
||||
// and it calls getExtraClasspath in its constructor
|
||||
// Not sure why WACL already exists...
|
||||
_wac.setExtraClasspath(buf.toString());
|
||||
wac.setExtraClasspath(buf.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,14 +153,25 @@ public class WebAppConfiguration implements Configuration {
|
||||
return rv;
|
||||
}
|
||||
|
||||
public void configureDefaults() {}
|
||||
public void configureWebApp() {}
|
||||
/** @since Jetty 7 */
|
||||
public void deconfigure(WebAppContext context) {}
|
||||
|
||||
/** @since Jetty 6 */
|
||||
public void deconfigureWebApp() {}
|
||||
|
||||
/** @since Jetty 6 */
|
||||
public void configureClassLoader() throws Exception {
|
||||
configureClassPath();
|
||||
/** @since Jetty 7 */
|
||||
public void configure(WebAppContext context) throws Exception {
|
||||
configureClassPath(context);
|
||||
}
|
||||
|
||||
/** @since Jetty 7 */
|
||||
public void cloneConfigure(WebAppContext template, WebAppContext context) {
|
||||
// no state, nothing to be done
|
||||
}
|
||||
|
||||
/** @since Jetty 7 */
|
||||
public void destroy(WebAppContext context) {}
|
||||
|
||||
/** @since Jetty 7 */
|
||||
public void preConfigure(WebAppContext context) {}
|
||||
|
||||
/** @since Jetty 7 */
|
||||
public void postConfigure(WebAppContext context) {}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@ import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.SecureDirectory;
|
||||
|
||||
import org.mortbay.jetty.Handler;
|
||||
import org.mortbay.jetty.Server;
|
||||
import org.mortbay.jetty.webapp.WebAppContext;
|
||||
import org.mortbay.jetty.handler.ContextHandler;
|
||||
import org.mortbay.jetty.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
|
||||
|
||||
/**
|
||||
@@ -56,7 +56,7 @@ public class WebAppStarter {
|
||||
File tmpdir = new SecureDirectory(ctx.getTempDir(), "jetty-work-" + appName + ctx.random().nextInt());
|
||||
WebAppContext wac = addWebApp(ctx, server, appName, warPath, tmpdir);
|
||||
//_log.debug("Loading war from: " + warPath);
|
||||
wac.setInitParams(INIT_PARAMS);
|
||||
LocaleWebAppHandler.setInitParams(wac, INIT_PARAMS);
|
||||
wac.start();
|
||||
}
|
||||
|
||||
@@ -155,6 +155,8 @@ public class WebAppStarter {
|
||||
return null;
|
||||
String path = '/'+ appName;
|
||||
for (int i = 0; i < handlers.length; i++) {
|
||||
if (!(handlers[i] instanceof ContextHandler))
|
||||
continue;
|
||||
ContextHandler ch = (ContextHandler) handlers[i];
|
||||
if (path.equals(ch.getContextPath()))
|
||||
return ch;
|
||||
|
||||
@@ -26,6 +26,11 @@
|
||||
*/
|
||||
ctx.updateManager().renderStatusHTML(out);
|
||||
|
||||
/*
|
||||
* Print out the status for the AppManager
|
||||
*/
|
||||
ctx.clientAppManager().renderStatusHTML(out);
|
||||
|
||||
/*
|
||||
* Print out the status for all the SessionKeyManagers
|
||||
*/
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<%
|
||||
// Let's make this easy...
|
||||
final Integer ERROR_CODE = (Integer) request.getAttribute(org.mortbay.jetty.servlet.ServletHandler.__J_S_ERROR_STATUS_CODE);
|
||||
final String ERROR_URI = (String) request.getAttribute(org.mortbay.jetty.servlet.ServletHandler.__J_S_ERROR_REQUEST_URI);
|
||||
final String ERROR_MESSAGE = (String) request.getAttribute(org.mortbay.jetty.servlet.ServletHandler.__J_S_ERROR_MESSAGE);
|
||||
final Integer ERROR_CODE = (Integer) request.getAttribute(org.eclipse.jetty.server.Dispatcher.ERROR_STATUS_CODE);
|
||||
final String ERROR_URI = (String) request.getAttribute(org.eclipse.jetty.server.Dispatcher.ERROR_REQUEST_URI);
|
||||
final String ERROR_MESSAGE = (String) request.getAttribute(org.eclipse.jetty.server.Dispatcher.ERROR_MESSAGE);
|
||||
if (ERROR_CODE != null && ERROR_MESSAGE != null) {
|
||||
// this is deprecated but we don't want sendError()
|
||||
response.setStatus(ERROR_CODE.intValue(), ERROR_MESSAGE);
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<%
|
||||
// Let's make this easy...
|
||||
final Integer ERROR_CODE = (Integer) request.getAttribute(org.mortbay.jetty.servlet.ServletHandler.__J_S_ERROR_STATUS_CODE);
|
||||
final String ERROR_URI = (String) request.getAttribute(org.mortbay.jetty.servlet.ServletHandler.__J_S_ERROR_REQUEST_URI);
|
||||
final String ERROR_MESSAGE = (String) request.getAttribute(org.mortbay.jetty.servlet.ServletHandler.__J_S_ERROR_MESSAGE);
|
||||
final Class ERROR_CLASS = (Class)request.getAttribute(org.mortbay.jetty.servlet.ServletHandler.__J_S_ERROR_EXCEPTION_TYPE);
|
||||
final Throwable ERROR_THROWABLE = (Throwable)request.getAttribute(org.mortbay.jetty.servlet.ServletHandler.__J_S_ERROR_EXCEPTION);
|
||||
final Integer ERROR_CODE = (Integer) request.getAttribute(org.eclipse.jetty.server.Dispatcher.ERROR_STATUS_CODE);
|
||||
final String ERROR_URI = (String) request.getAttribute(org.eclipse.jetty.server.Dispatcher.ERROR_REQUEST_URI);
|
||||
final String ERROR_MESSAGE = (String) request.getAttribute(org.eclipse.jetty.server.Dispatcher.ERROR_MESSAGE);
|
||||
final Class ERROR_CLASS = (Class)request.getAttribute(org.eclipse.jetty.server.Dispatcher.ERROR_EXCEPTION_TYPE);
|
||||
final Throwable ERROR_THROWABLE = (Throwable)request.getAttribute(org.eclipse.jetty.server.Dispatcher.ERROR_EXCEPTION);
|
||||
if (ERROR_CODE != null && ERROR_MESSAGE != null) {
|
||||
// this is deprecated but we don't want sendError()
|
||||
response.setStatus(ERROR_CODE.intValue(), ERROR_MESSAGE);
|
||||
|
||||
@@ -60,15 +60,15 @@ if ( !rendered && ((rs != null) || fakeBw) ) {
|
||||
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 hideGrid = Boolean.valueOf(""+request.getParameter("hideGrid")).booleanValue();
|
||||
boolean hideTitle = Boolean.valueOf(""+request.getParameter("hideTitle")).booleanValue();
|
||||
boolean showEvents = Boolean.valueOf(""+request.getParameter("showEvents")).booleanValue();
|
||||
boolean hideLegend = Boolean.parseBoolean((String) request.getParameter("hideLegend"));
|
||||
boolean hideGrid = Boolean.parseBoolean((String) request.getParameter("hideGrid"));
|
||||
boolean hideTitle = Boolean.parseBoolean((String) request.getParameter("hideTitle"));
|
||||
boolean showEvents = Boolean.parseBoolean((String) request.getParameter("showEvents"));
|
||||
boolean showCredit = false;
|
||||
if (request.getParameter("showCredit") != null)
|
||||
showCredit = Boolean.valueOf(""+request.getParameter("showCredit")).booleanValue();
|
||||
showCredit = Boolean.parseBoolean((String) request.getParameter("showCredit"));
|
||||
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, end, showCredit);
|
||||
else
|
||||
rendered = net.i2p.router.web.StatSummarizer.instance().renderPng(rate, cout, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount, end, showCredit);
|
||||
}
|
||||
|
||||
@@ -18,11 +18,15 @@ import java.net.InetSocketAddress;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.*;
|
||||
import static net.i2p.app.ClientAppState.*;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
@@ -33,10 +37,14 @@ import net.i2p.util.Log;
|
||||
*
|
||||
* @author human
|
||||
*/
|
||||
public class SAMBridge implements Runnable {
|
||||
private final static Log _log = new Log(SAMBridge.class);
|
||||
private final ServerSocketChannel serverSocket;
|
||||
public class SAMBridge implements Runnable, ClientApp {
|
||||
private final Log _log;
|
||||
private volatile ServerSocketChannel serverSocket;
|
||||
private final String _listenHost;
|
||||
private final int _listenPort;
|
||||
private final Properties i2cpProps;
|
||||
private volatile Thread _runner;
|
||||
|
||||
/**
|
||||
* filename in which the name to private key mapping should
|
||||
* be stored (and loaded from)
|
||||
@@ -50,6 +58,10 @@ public class SAMBridge implements Runnable {
|
||||
|
||||
private volatile boolean acceptConnections = true;
|
||||
|
||||
private final ClientAppManager _mgr;
|
||||
private final String[] _args;
|
||||
private volatile ClientAppState _state = UNINITIALIZED;
|
||||
|
||||
private static final int SAM_LISTENPORT = 7656;
|
||||
|
||||
public static final String DEFAULT_SAM_KEYFILE = "sam.keys";
|
||||
@@ -63,9 +75,40 @@ public class SAMBridge implements Runnable {
|
||||
protected static final String DEFAULT_DATAGRAM_HOST = "0.0.0.0";
|
||||
protected static final String DEFAULT_DATAGRAM_PORT = "7655";
|
||||
|
||||
|
||||
/**
|
||||
* For ClientApp interface.
|
||||
* Recommended constructor for external use.
|
||||
* Does NOT open the listener socket or start threads; caller must call startup()
|
||||
*
|
||||
* @param mgr may be null
|
||||
* @param args non-null
|
||||
* @throws Exception on bad args
|
||||
* @since 0.9.6
|
||||
*/
|
||||
public SAMBridge(I2PAppContext context, ClientAppManager mgr, String[] args) throws Exception {
|
||||
_log = context.logManager().getLog(SAMBridge.class);
|
||||
_mgr = mgr;
|
||||
_args = args;
|
||||
Options options = getOptions(args);
|
||||
_listenHost = options.host;
|
||||
_listenPort = options.port;
|
||||
persistFilename = options.keyFile;
|
||||
nameToPrivKeys = new HashMap<String,String>(8);
|
||||
this.i2cpProps = options.opts;
|
||||
_state = INITIALIZED;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build a new SAM bridge.
|
||||
* NOT recommended for external use.
|
||||
*
|
||||
* Opens the listener socket but does NOT start the thread, and there's no
|
||||
* way to do that externally.
|
||||
* Use main(), or use the other constructor and call startup().
|
||||
*
|
||||
* Deprecated for external use, to be made private.
|
||||
*
|
||||
* @param listenHost hostname to listen for SAM connections on ("0.0.0.0" for all)
|
||||
* @param listenPort port number to listen for SAM connections on
|
||||
@@ -74,31 +117,43 @@ public class SAMBridge implements Runnable {
|
||||
* @throws RuntimeException if a server socket can't be opened
|
||||
*/
|
||||
public SAMBridge(String listenHost, int listenPort, Properties i2cpProps, String persistFile) {
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(SAMBridge.class);
|
||||
_mgr = null;
|
||||
_args = new String[] {listenHost, Integer.toString(listenPort) }; // placeholder
|
||||
_listenHost = listenHost;
|
||||
_listenPort = listenPort;
|
||||
persistFilename = persistFile;
|
||||
nameToPrivKeys = new HashMap<String,String>(8);
|
||||
loadKeys();
|
||||
try {
|
||||
if ( (listenHost != null) && !("0.0.0.0".equals(listenHost)) ) {
|
||||
serverSocket = ServerSocketChannel.open();
|
||||
serverSocket.socket().bind(new InetSocketAddress(listenHost, listenPort));
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("SAM bridge listening on "
|
||||
+ listenHost + ":" + listenPort);
|
||||
} else {
|
||||
serverSocket = ServerSocketChannel.open();
|
||||
serverSocket.socket().bind(new InetSocketAddress(listenPort));
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("SAM bridge listening on 0.0.0.0:" + listenPort);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
openSocket();
|
||||
} catch (IOException e) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error starting SAM bridge on "
|
||||
+ (listenHost == null ? "0.0.0.0" : listenHost)
|
||||
+ ":" + listenPort, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
this.i2cpProps = i2cpProps;
|
||||
_state = INITIALIZED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.6
|
||||
*/
|
||||
private void openSocket() throws IOException {
|
||||
if ( (_listenHost != null) && !("0.0.0.0".equals(_listenHost)) ) {
|
||||
serverSocket = ServerSocketChannel.open();
|
||||
serverSocket.socket().bind(new InetSocketAddress(_listenHost, _listenPort));
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("SAM bridge listening on "
|
||||
+ _listenHost + ":" + _listenPort);
|
||||
} else {
|
||||
serverSocket = ServerSocketChannel.open();
|
||||
serverSocket.socket().bind(new InetSocketAddress(_listenPort));
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("SAM bridge listening on 0.0.0.0:" + _listenPort);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,8 +260,89 @@ public class SAMBridge implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
////// begin ClientApp interface, use only if using correct construtor
|
||||
|
||||
/**
|
||||
* @since 0.9.6
|
||||
*/
|
||||
public synchronized void startup() throws IOException {
|
||||
if (_state != INITIALIZED)
|
||||
return;
|
||||
changeState(STARTING);
|
||||
loadKeys();
|
||||
try {
|
||||
openSocket();
|
||||
} catch (IOException e) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error starting SAM bridge on "
|
||||
+ (_listenHost == null ? "0.0.0.0" : _listenHost)
|
||||
+ ":" + _listenPort, e);
|
||||
changeState(START_FAILED, e);
|
||||
throw e;
|
||||
}
|
||||
startThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does NOT stop existing sessions.
|
||||
* @since 0.9.6
|
||||
*/
|
||||
public synchronized void shutdown(String[] args) {
|
||||
if (_state != RUNNING)
|
||||
return;
|
||||
changeState(STOPPING);
|
||||
acceptConnections = false;
|
||||
if (_runner != null)
|
||||
_runner.interrupt();
|
||||
else
|
||||
changeState(STOPPED);
|
||||
// TODO does not stop active connections / sessions
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.6
|
||||
*/
|
||||
public ClientAppState getState() {
|
||||
return _state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.6
|
||||
*/
|
||||
public String getName() {
|
||||
return "SAM";
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.6
|
||||
*/
|
||||
public String getDisplayName() {
|
||||
return "SAM " + Arrays.toString(_args);
|
||||
}
|
||||
|
||||
////// end ClientApp interface
|
||||
////// begin ClientApp helpers
|
||||
|
||||
/**
|
||||
* @since 0.9.6
|
||||
*/
|
||||
private void changeState(ClientAppState state) {
|
||||
changeState(state, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.6
|
||||
*/
|
||||
private synchronized void changeState(ClientAppState state, Exception e) {
|
||||
_state = state;
|
||||
if (_mgr != null)
|
||||
_mgr.notify(this, state, null, e);
|
||||
}
|
||||
|
||||
////// end ClientApp helpers
|
||||
|
||||
static class HelpRequested extends Exception {static final long serialVersionUID=0x1;}
|
||||
|
||||
|
||||
/**
|
||||
* Usage:
|
||||
* <pre>SAMBridge [ keyfile [listenHost ] listenPort [ name=val ]* ]</pre>
|
||||
@@ -219,39 +355,26 @@ public class SAMBridge implements Runnable {
|
||||
* @param args [ keyfile [ listenHost ] listenPort [ name=val ]* ]
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
String keyfile = DEFAULT_SAM_KEYFILE;
|
||||
int port = SAM_LISTENPORT;
|
||||
String host = DEFAULT_TCP_HOST;
|
||||
Properties opts = null;
|
||||
if (args.length > 0) {
|
||||
try {
|
||||
opts = parseOptions(args, 0);
|
||||
keyfile = args[0];
|
||||
int portIndex = 1;
|
||||
try {
|
||||
if (args.length>portIndex) port = Integer.parseInt(args[portIndex]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
host = args[portIndex];
|
||||
portIndex++;
|
||||
try {
|
||||
if (args.length>portIndex) port = Integer.parseInt(args[portIndex]);
|
||||
} catch (NumberFormatException nfe1) {
|
||||
try {
|
||||
port = Integer.parseInt(opts.getProperty(SAMBridge.PROP_TCP_PORT, SAMBridge.DEFAULT_TCP_PORT));
|
||||
host = opts.getProperty(SAMBridge.PROP_TCP_HOST, SAMBridge.DEFAULT_TCP_HOST);
|
||||
} catch (NumberFormatException e) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (HelpRequested e) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Options options = getOptions(args);
|
||||
SAMBridge bridge = new SAMBridge(options.host, options.port, options.opts, options.keyFile);
|
||||
bridge.startThread();
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
usage();
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
usage();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
SAMBridge bridge = new SAMBridge(host, port, opts, keyfile);
|
||||
I2PAppThread t = new I2PAppThread(bridge, "SAMListener");
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.6
|
||||
*/
|
||||
private void startThread() {
|
||||
I2PAppThread t = new I2PAppThread(this, "SAMListener");
|
||||
if (Boolean.parseBoolean(System.getProperty("sam.shutdownOnOOM"))) {
|
||||
t.addOOMEventThreadListener(new I2PAppThread.OOMEventListener() {
|
||||
public void outOfMemory(OutOfMemoryError err) {
|
||||
@@ -262,6 +385,58 @@ public class SAMBridge implements Runnable {
|
||||
});
|
||||
}
|
||||
t.start();
|
||||
_runner = t;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.6
|
||||
*/
|
||||
private static class Options {
|
||||
private final String host, keyFile;
|
||||
private final int port;
|
||||
private final Properties opts;
|
||||
|
||||
public Options(String host, int port, Properties opts, String keyFile) {
|
||||
this.host = host; this.port = port; this.opts = opts; this.keyFile = keyFile;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage:
|
||||
* <pre>SAMBridge [ keyfile [listenHost ] listenPort [ name=val ]* ]</pre>
|
||||
* or:
|
||||
* <pre>SAMBridge [ name=val ]* </pre>
|
||||
*
|
||||
* name=val options are passed to the I2CP code to build a session,
|
||||
* allowing the bridge to specify an alternate I2CP host and port, tunnel
|
||||
* depth, etc.
|
||||
* @param args [ keyfile [ listenHost ] listenPort [ name=val ]* ]
|
||||
* @return non-null Options or throws Exception
|
||||
* @since 0.9.6
|
||||
*/
|
||||
private static Options getOptions(String args[]) throws Exception {
|
||||
String keyfile = DEFAULT_SAM_KEYFILE;
|
||||
int port = SAM_LISTENPORT;
|
||||
String host = DEFAULT_TCP_HOST;
|
||||
Properties opts = null;
|
||||
if (args.length > 0) {
|
||||
opts = parseOptions(args, 0);
|
||||
keyfile = args[0];
|
||||
int portIndex = 1;
|
||||
try {
|
||||
if (args.length>portIndex) port = Integer.parseInt(args[portIndex]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
host = args[portIndex];
|
||||
portIndex++;
|
||||
try {
|
||||
if (args.length>portIndex) port = Integer.parseInt(args[portIndex]);
|
||||
} catch (NumberFormatException nfe1) {
|
||||
port = Integer.parseInt(opts.getProperty(SAMBridge.PROP_TCP_PORT, SAMBridge.DEFAULT_TCP_PORT));
|
||||
host = opts.getProperty(SAMBridge.PROP_TCP_HOST, SAMBridge.DEFAULT_TCP_HOST);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Options(host, port, opts, keyfile);
|
||||
}
|
||||
|
||||
private static Properties parseOptions(String args[], int startArgs) throws HelpRequested {
|
||||
@@ -308,6 +483,9 @@ public class SAMBridge implements Runnable {
|
||||
|
||||
public void run() {
|
||||
if (serverSocket == null) return;
|
||||
changeState(RUNNING);
|
||||
if (_mgr != null)
|
||||
_mgr.register(this);
|
||||
try {
|
||||
while (acceptConnections) {
|
||||
SocketChannel s = serverSocket.accept();
|
||||
@@ -353,11 +531,16 @@ public class SAMBridge implements Runnable {
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: Handler threads are not saved or tracked and cannot be stopped
|
||||
new I2PAppThread(new HelloHandler(s,this), "HelloHandler").start();
|
||||
}
|
||||
changeState(STOPPING);
|
||||
} catch (Exception e) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
if (acceptConnections)
|
||||
_log.error("Unexpected error while listening for connections", e);
|
||||
else
|
||||
e = null;
|
||||
changeState(STOPPING, e);
|
||||
} finally {
|
||||
try {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -365,6 +548,7 @@ public class SAMBridge implements Runnable {
|
||||
if (serverSocket != null)
|
||||
serverSocket.close();
|
||||
} catch (IOException e) {}
|
||||
changeState(STOPPED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,9 +101,9 @@ class PacketHandler {
|
||||
|
||||
Connection con = (sendId > 0 ? _manager.getConnectionByInboundId(sendId) : null);
|
||||
if (con != null) {
|
||||
receiveKnownCon(con, packet);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
displayPacket(packet, "RECV", "wsize " + con.getOptions().getWindowSize() + " rto " + con.getOptions().getRTO());
|
||||
receiveKnownCon(con, packet);
|
||||
} else {
|
||||
receiveUnknownCon(packet, sendId, queueIfNoConn);
|
||||
displayPacket(packet, "UNKN", null);
|
||||
|
||||
@@ -49,10 +49,10 @@ public class PcapWriter {
|
||||
0, 2, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, (byte) 0xff, (byte) 0xff, 0, 0, 0, 1 };
|
||||
|
||||
/** dummy macs and ethertype */
|
||||
/** dummy macs, IPv4 ethertype */
|
||||
private static final byte[] MAC_HEADER = { 1, 2, 3, 4, 5, 6,
|
||||
1, 2, 3, 4, 5, 6,
|
||||
(byte) 0x80, 0 };
|
||||
(byte) 0x08, 0 };
|
||||
private static final byte[] IP_HEADER_1 = { 0x45, 0 }; // the length goes after this
|
||||
private static final byte[] IP_HEADER_2 = { 0x12, 0x34, 0x40, 0, 64, 6 }; // ID, flags, TTL and TCP
|
||||
private static final byte[] UNK_IP = { (byte) 0xff, 0, 0, 0};
|
||||
@@ -265,19 +265,24 @@ public class PcapWriter {
|
||||
long window = ConnectionOptions.INITIAL_WINDOW_SIZE;
|
||||
long msgSize = ConnectionOptions.DEFAULT_MAX_MESSAGE_SIZE;
|
||||
if (con != null) {
|
||||
// calculate the receive window, which doesn't have an exact streaming equivalent
|
||||
if (isInbound) {
|
||||
// Inbound pkt: his rcv buffer ~= our outbound window
|
||||
// try to represent what he thinks the window is, we don't really know
|
||||
// this isn't really right, the lastsendid can get way ahead
|
||||
window = acked + con.getOptions().getWindowSize() - con.getLastSendId();
|
||||
window = con.getLastSendId() + con.getOptions().getWindowSize() - acked;
|
||||
} else {
|
||||
// Ourbound pkt: our rcv buffer ~= his outbound window
|
||||
// TODO just use a recent high unackedIn count?
|
||||
// following is from ConnectionPacketHandler
|
||||
// this is not interesting, we have lots of buffers
|
||||
long ready = con.getInputStream().getHighestReadyBockId();
|
||||
int available = con.getOptions().getInboundBufferSize() - con.getInputStream().getTotalReadySize();
|
||||
int allowedBlocks = available/con.getOptions().getMaxMessageSize();
|
||||
window = (ready + allowedBlocks) - pkt.getSequenceNum();
|
||||
}
|
||||
if (window < 0)
|
||||
window = 0;
|
||||
if (window <= 1)
|
||||
window = 2; // TCP min
|
||||
msgSize = con.getOptions().getMaxMessageSize();
|
||||
}
|
||||
// messages -> bytes
|
||||
|
||||
@@ -187,7 +187,7 @@ public class AddressBean
|
||||
public String getSource() {
|
||||
String rv = getProp("s");
|
||||
if (rv.startsWith("http://"))
|
||||
rv = "<a href=\"" + rv + "\">" + rv + "</a>";
|
||||
rv = "<a href=\"" + rv + "\" target=\"_top\">" + rv + "</a>";
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
@@ -155,9 +155,9 @@ ${book.loadBookMessages}
|
||||
<c:if test="${book.master || book.router || book.published || book.private}">
|
||||
<td class="checkbox"><input type="checkbox" name="checked" value="${addr.name}" title="<%=intl._("Mark for deletion")%>"></td>
|
||||
</c:if>
|
||||
<td class="names"><a href="http://${addr.name}/">${addr.displayName}</a>
|
||||
<td class="names"><a href="http://${addr.name}/" target="_top">${addr.displayName}</a>
|
||||
</td><td class="names">
|
||||
<span class="addrhlpr"><a href="http://${addr.b32}/" title="<%=intl._("Base 32 address")%>">b32</a></span>
|
||||
<span class="addrhlpr"><a href="http://${addr.b32}/" target="_top" title="<%=intl._("Base 32 address")%>">b32</a></span>
|
||||
</td><td class="names">
|
||||
<span class="addrhlpr"><a href="details?h=${addr.name}" title="<%=intl._("More information on this entry")%>"><%=intl._("details")%></a></span>
|
||||
</td>
|
||||
@@ -204,7 +204,7 @@ ${book.loadBookMessages}
|
||||
|
||||
<div id="footer">
|
||||
<hr>
|
||||
<p class="footer">susidns v${version.version} © <a href="${version.url}">susi</a> 2005</p>
|
||||
<p class="footer">susidns v${version.version} © <a href="${version.url}" target="_top">susi</a> 2005</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
</div>
|
||||
<div id="footer">
|
||||
<hr>
|
||||
<p class="footer">susidns v${version.version} © <a href="${version.url}">susi</a> 2005 </p>
|
||||
<p class="footer">susidns v${version.version} © <a href="${version.url}" target="_top">susi</a> 2005 </p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -83,25 +83,25 @@
|
||||
<table class="book" cellspacing="0" cellpadding="5">
|
||||
<tr class="list${book.trClass}">
|
||||
<td><%=intl._("Host Name")%></td>
|
||||
<td><a href="http://<%=addr.getName()%>/"><%=addr.getDisplayName()%></a></td>
|
||||
<td><a href="http://<%=addr.getName()%>/" target="_top"><%=addr.getDisplayName()%></a></td>
|
||||
</tr><tr class="list${book.trClass}">
|
||||
<%
|
||||
if (addr.isIDN()) {
|
||||
%>
|
||||
<td><%=intl._("Encoded Name")%></td>
|
||||
<td><a href="http://<%=addr.getName()%>/"><%=addr.getName()%></a></td>
|
||||
<td><a href="http://<%=addr.getName()%>/" target="_top"><%=addr.getName()%></a></td>
|
||||
</tr><tr class="list${book.trClass}">
|
||||
<%
|
||||
}
|
||||
%>
|
||||
<td><%=intl._("Base 32 Address")%></td>
|
||||
<td><a href="http://<%=b32%>/"><%=b32%></a></td>
|
||||
<td><a href="http://<%=b32%>/" target="_top"><%=b32%></a></td>
|
||||
</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><a href="http://<%=addr.getName()%>/?i2paddresshelper=<%=addr.getDestination()%>"><%=intl._("link")%></a></td>
|
||||
<td><a href="http://<%=addr.getName()%>/?i2paddresshelper=<%=addr.getDestination()%>" target="_top"><%=intl._("link")%></a></td>
|
||||
</tr><tr class="list${book.trClass}">
|
||||
<td><%=intl._("Public Key")%></td>
|
||||
<td><%=intl._("ElGamal 2048 bit")%></td>
|
||||
@@ -145,7 +145,7 @@
|
||||
%>
|
||||
<div id="footer">
|
||||
<hr>
|
||||
<p class="footer">susidns v${version.version} © <a href="${version.url}">susi</a> 2005</p>
|
||||
<p class="footer">susidns v${version.version} © <a href="${version.url}" target="_top">susi</a> 2005</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
<%=intl._("Subscribing to additional sites is easy, just add them to your <a href=\"subscriptions\">subscriptions</a> file.")%>
|
||||
</p>
|
||||
<p>
|
||||
<%=intl._("For more information on naming in I2P, see <a href=\"http://www.i2p2.i2p/naming.html\">the overview on www.i2p2.i2p</a>.")%>
|
||||
<%=intl._("For more information on naming in I2P, see <a href=\"http://www.i2p2.i2p/naming.html\" target=\"_top\">the overview on www.i2p2.i2p</a>.")%>
|
||||
</p>
|
||||
<h3><%=intl._("How does the addressbook application work?")%></h3>
|
||||
<p>
|
||||
@@ -90,7 +90,7 @@
|
||||
</div>
|
||||
<div id="footer">
|
||||
<hr>
|
||||
<p class="footer">susidns v${version.version} © <a href="${version.url}">susi</a> 2005</p>
|
||||
<p class="footer">susidns v${version.version} © <a href="${version.url}" target="_top">susi</a> 2005</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -83,12 +83,12 @@
|
||||
<%=intl._("Those URLs refer to published hosts.txt files.")%>
|
||||
<%=intl._("The default subscription is the hosts.txt from www.i2p2.i2p, which is updated infrequently.")%>
|
||||
<%=intl._("So it is a good idea to add additional subscriptions to sites that have the latest addresses.")%>
|
||||
<a href="http://www.i2p2.i2p/faq.html#subscriptions"><%=intl._("See the FAQ for a list of subscription URLs.")%></a>
|
||||
<a href="http://www.i2p2.i2p/faq.html#subscriptions" target="_top"><%=intl._("See the FAQ for a list of subscription URLs.")%></a>
|
||||
</p>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<hr>
|
||||
<p class="footer">susidns v${version.version} © <a href="${version.url}">susi</a> 2005</p>
|
||||
<p class="footer">susidns v${version.version} © <a href="${version.url}" target="_top">susi</a> 2005</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<pathelement location="../jetty/jettylib/javax.servlet.jar" />
|
||||
<pathelement location="../jetty/jettylib/org.mortbay.jetty.jar" />
|
||||
<pathelement location="../jetty/jettylib/jetty-util.jar" />
|
||||
<pathelement location="../jetty/jettylib/jetty-http.jar" />
|
||||
<pathelement location="../../core/java/build/i2p.jar" />
|
||||
</classpath>
|
||||
</javac>
|
||||
|
||||
@@ -29,10 +29,10 @@ import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
//import org.apache.commons.logging.Log;
|
||||
//import org.mortbay.log.LogFactory;
|
||||
import org.mortbay.jetty.HttpHeaders;
|
||||
import org.eclipse.jetty.http.HttpHeaders;
|
||||
import org.eclipse.jetty.util.MultiMap;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.mortbay.util.LineInput;
|
||||
import org.mortbay.util.MultiMap;
|
||||
import org.mortbay.util.StringUtil;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Multipart Form Data request.
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# Note: Include the trailing slash! Don't surround the URL in quotes!
|
||||
javasedocs.url=http://docs.oracle.com/javase/6/docs/api/
|
||||
javaeedocs.url=http://docs.oracle.com/javaee/6/api/
|
||||
jettydocs.url=http://jetty.codehaus.org/jetty/jetty-6/apidocs/
|
||||
jettydocs.url=http://download.eclipse.org/jetty/stable-7/apidocs/
|
||||
jrobindocs.url=http://docs.i2p-projekt.de/jrobin/javadoc/
|
||||
wrapperdocs.url=http://wrapper.tanukisoftware.com/jdoc/
|
||||
# these are only for unit test javadocs
|
||||
|
||||
17
build.xml
17
build.xml
@@ -472,7 +472,7 @@
|
||||
<group title="I2PSnark Application" packages="org.klomp.snark:org.klomp.snark.*:net.i2p.kademlia" />
|
||||
<group title="I2PTunnel Application" packages="net.i2p.i2ptunnel:net.i2p.i2ptunnel.*" />
|
||||
<group title="Installer Utilities" packages="net.i2p.installer" />
|
||||
<group title="Jetty Logging" packages="net.i2p.jetty" />
|
||||
<group title="Jetty Starter and Logging" packages="net.i2p.jetty" />
|
||||
<group title="SAM Bridge" packages="net.i2p.sam" />
|
||||
<group title="SAM Demos" packages="net.i2p.sam.client" />
|
||||
<group title="SusiDNS Application" packages="i2p.susi.dns" />
|
||||
@@ -501,10 +501,13 @@
|
||||
<classpath>
|
||||
<pathelement location="apps/jetty/jettylib/org.mortbay.jetty.jar" />
|
||||
<pathelement location="apps/jetty/jettylib/javax.servlet.jar" />
|
||||
<pathelement location="apps/jetty/jettylib/jetty-sslengine.jar" />
|
||||
<pathelement location="apps/jetty/jettylib/jetty-http.jar" />
|
||||
<pathelement location="apps/jetty/jettylib/jetty-security.jar" />
|
||||
<pathelement location="apps/jetty/jettylib/jetty-servlet.jar" />
|
||||
<pathelement location="apps/jetty/jettylib/jetty-start.jar" />
|
||||
<pathelement location="apps/jetty/jettylib/jetty-java5-threadpool.jar" />
|
||||
<pathelement location="apps/jetty/jettylib/jetty-util.jar" />
|
||||
<pathelement location="apps/jetty/jettylib/jetty-webapp.jar" />
|
||||
<pathelement location="apps/jetty/jettylib/jetty-xml.jar" />
|
||||
<pathelement location="apps/jetty/jettylib/jsp-api.jar" />
|
||||
<pathelement location="apps/systray/java/lib/systray4j.jar" />
|
||||
<pathelement location="apps/jrobin/jrobin-1.5.9.1.jar" />
|
||||
@@ -1040,11 +1043,11 @@
|
||||
<!-- *nix here -->
|
||||
<exec executable="sh" osfamily="unix" failonerror="true">
|
||||
<arg value="-c" />
|
||||
<arg value="for i in pkg-temp/lib/*.jar pkg-temp/webapps/*war; do if [ $i = pkg-temp/lib/jasper-compiler.jar -o $i = pkg-temp/lib/jbigi.jar ]; then continue; fi; echo pack200 $i; mv $i $i.jar; pack200 -g $i.pack $i.jar; rm -f $i.jar; done" />
|
||||
<arg value="for i in pkg-temp/lib/*.jar pkg-temp/webapps/*war; do if [ $i = pkg-temp/lib/jasper-compiler.jar -o $i = pkg-temp/lib/jbigi.jar -o $i = pkg-temp/lib/jetty-java5-threadpool.jar -o $i = pkg-temp/lib/jetty-sslengine.jar ]; then continue; fi; echo pack200 $i; mv $i $i.jar; pack200 -g $i.pack $i.jar; rm -f $i.jar; done" />
|
||||
</exec>
|
||||
<exec executable="sh" osfamily="mac" failonerror="true">
|
||||
<arg value="-c" />
|
||||
<arg value="for i in pkg-temp/lib/*.jar pkg-temp/webapps/*war; do if [ $i = pkg-temp/lib/jasper-compiler.jar -o $i = pkg-temp/lib/jbigi.jar ]; then continue; fi; echo pack200 $i; mv $i $i.jar; pack200 -g $i.pack $i.jar; rm -f $i.jar; done" />
|
||||
<arg value="for i in pkg-temp/lib/*.jar pkg-temp/webapps/*war; do if [ $i = pkg-temp/lib/jasper-compiler.jar -o $i = pkg-temp/lib/jbigi.jar -o $i = pkg-temp/lib/jetty-java5-threadpool.jar -o $i = pkg-temp/lib/jetty-sslengine.jar ]; then continue; fi; echo pack200 $i; mv $i $i.jar; pack200 -g $i.pack $i.jar; rm -f $i.jar; done" />
|
||||
</exec>
|
||||
<!-- windoz here : i admit, i hate escaped symbols in xml, indeed = =! -->
|
||||
<exec executable="cmd" osfamily="windows" failonerror="true">
|
||||
@@ -1153,13 +1156,13 @@
|
||||
<!-- We have to package the new eepsite files for MigrateJetty.java, but we
|
||||
can't overwrite an existing eepsite dir in a non-split configuration.
|
||||
-->
|
||||
<copy todir="pkg-temp/eepsite-jetty6" >
|
||||
<copy todir="pkg-temp/eepsite-jetty7" >
|
||||
<fileset dir="installer/resources/eepsite" includes="*.xml contexts/* etc/*" />
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<target name="delete-j6-update">
|
||||
<delete dir="pkg-temp/eepsite-jetty6" />
|
||||
<delete dir="pkg-temp/eepsite-jetty7" />
|
||||
</target>
|
||||
|
||||
<!-- Jetty 6 I2P logging addons, not really fixes -->
|
||||
|
||||
@@ -36,23 +36,28 @@ public interface ClientApp {
|
||||
* If previously running, client must call ClientAppManager.notify() at least once within this
|
||||
* method to change the state to STOPPING or STOPPED.
|
||||
* May be called multiple times on the same object, in any state.
|
||||
*
|
||||
* @param args generally null but could be stopArgs from clients.config
|
||||
*/
|
||||
public void shutdown(String[] args) throws Throwable;
|
||||
|
||||
/**
|
||||
* The current state of the ClientApp.
|
||||
* @return non-null
|
||||
*/
|
||||
public ClientAppState getState();
|
||||
|
||||
/**
|
||||
* The generic name of the ClientApp, used for registration,
|
||||
* e.g. "console". Do not translate.
|
||||
* @return non-null
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* The dislplay name of the ClientApp, used in user interfaces.
|
||||
* The app must translate.
|
||||
* @return non-null
|
||||
*/
|
||||
public String getDisplayName();
|
||||
}
|
||||
|
||||
@@ -105,20 +105,19 @@ public class ElGamalAESEngine {
|
||||
boolean wasExisting = false;
|
||||
if (key != null) {
|
||||
//if (_log.shouldLog(Log.DEBUG)) _log.debug("Key is known for tag " + st);
|
||||
long id = _context.random().nextLong();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(id + ": Decrypting existing session encrypted with tag: " + st.toString() + ": key: " + key.toBase64() + ": " + data.length + " bytes " /* + Base64.encode(data, 0, 64) */ );
|
||||
_log.debug("Decrypting existing session encrypted with tag: " + st.toString() + ": key: " + key.toBase64() + ": " + data.length + " bytes " /* + Base64.encode(data, 0, 64) */ );
|
||||
|
||||
decrypted = decryptExistingSession(data, key, targetPrivateKey, foundTags, usedKey, foundKey);
|
||||
if (decrypted != null) {
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.decryptExistingSession");
|
||||
if ( (!foundTags.isEmpty()) && (_log.shouldLog(Log.DEBUG)) )
|
||||
_log.debug(id + ": ElG/AES decrypt success with " + st + ": found tags: " + foundTags);
|
||||
_log.debug("ElG/AES decrypt success with " + st + ": found tags: " + foundTags);
|
||||
wasExisting = true;
|
||||
} else {
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.decryptFailed");
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
_log.warn(id + ": ElG decrypt fail: known tag [" + st + "], failed decrypt");
|
||||
_log.warn("ElG decrypt fail: known tag [" + st + "], failed decrypt");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -568,6 +567,7 @@ public class ElGamalAESEngine {
|
||||
* - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0
|
||||
* </pre>
|
||||
*
|
||||
* @param target unused, this is AES encrypt only using the session key and tag
|
||||
* @param tagsForDelivery session tags to be associated with the key or null;
|
||||
* 200 max enforced at receiver
|
||||
*/
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The central resource coordinating updates.
|
||||
@@ -34,6 +35,7 @@ public interface UpdateManager {
|
||||
|
||||
/**
|
||||
* Called by the Checker, either after check() was called, or it found out on its own.
|
||||
* Use this if there is only one UpdateMethod; otherwise use the Map method below.
|
||||
*
|
||||
* @param newsSource who told us
|
||||
* @param id plugin name for plugins, ignored otherwise
|
||||
@@ -48,6 +50,23 @@ public interface UpdateManager {
|
||||
UpdateMethod method, List<URI> updateSources,
|
||||
String newVersion, String minVersion);
|
||||
|
||||
/**
|
||||
* Called by the Checker, either after check() was called, or it found out on its own.
|
||||
* Checkers must use this method if there are multiple UpdateMethods discoverd simultaneously.
|
||||
*
|
||||
* @param newsSource who told us
|
||||
* @param id plugin name for plugins, ignored otherwise
|
||||
* @param sourceMap Mapping of methods to sources
|
||||
* @param newVersion The new version available
|
||||
* @param minVersion The minimum installed version to be able to update to newVersion
|
||||
* @return true if we didn't know already
|
||||
* @since 0.9.6
|
||||
*/
|
||||
public boolean notifyVersionAvailable(UpdateTask task, URI newsSource,
|
||||
UpdateType type, String id,
|
||||
Map<UpdateMethod, List<URI>> sourceMap,
|
||||
String newVersion, String minVersion);
|
||||
|
||||
/**
|
||||
* Called by the Checker after check() was called and all notifyVersionAvailable() callbacks are finished
|
||||
* @param newer notifyVersionAvailable was called
|
||||
|
||||
@@ -625,6 +625,9 @@ public class LogManager {
|
||||
rv.setProperty(PROP_FORMAT, new String(_format));
|
||||
rv.setProperty(PROP_DATEFORMAT, _dateFormatPattern);
|
||||
rv.setProperty(PROP_DISPLAYONSCREEN, Boolean.toString(_displayOnScreen));
|
||||
rv.setProperty(PROP_DROP, Boolean.toString(_dropOnOverflow));
|
||||
rv.setProperty(PROP_DUP, Boolean.toString(_dropDuplicates));
|
||||
rv.setProperty(PROP_LOG_BUFFER_SIZE, Integer.toString(_logBufferSize));
|
||||
|
||||
// prior to 0.9.5, override prop trumped config file
|
||||
// as of 0.9.5, override prop trumps config file only if config file is set to default,
|
||||
|
||||
@@ -18,14 +18,25 @@ public abstract class SystemVersion {
|
||||
private static final boolean _isAndroid;
|
||||
private static final boolean _isApache;
|
||||
private static final boolean _isGNU;
|
||||
private static final boolean _is64 = "64".equals(System.getProperty("sun.arch.data.model")) ||
|
||||
System.getProperty("os.arch").contains("64");
|
||||
private static final boolean _is64;
|
||||
private static final boolean _hasWrapper = System.getProperty("wrapper.version") != null;
|
||||
|
||||
private static final boolean _oneDotSix;
|
||||
private static final int _androidSDK;
|
||||
|
||||
static {
|
||||
boolean is64 = "64".equals(System.getProperty("sun.arch.data.model")) ||
|
||||
System.getProperty("os.arch").contains("64");
|
||||
if (_isWin && !is64) {
|
||||
// http://stackoverflow.com/questions/4748673/how-can-i-check-the-bitness-of-my-os-using-java-j2se-not-os-arch
|
||||
// http://blogs.msdn.com/b/david.wang/archive/2006/03/26/howto-detect-process-bitness.aspx
|
||||
String arch = System.getenv("PROCESSOR_ARCHITECTURE");
|
||||
String wow64Arch = System.getenv("PROCESSOR_ARCHITEW6432");
|
||||
is64 = (arch != null && arch.endsWith("64")) ||
|
||||
(wow64Arch != null && wow64Arch.endsWith("64"));
|
||||
}
|
||||
_is64 = is64;
|
||||
|
||||
String vendor = System.getProperty("java.vendor");
|
||||
_isAndroid = vendor.contains("Android");
|
||||
_isApache = vendor.startsWith("Apache");
|
||||
|
||||
28
debian/changelog
vendored
28
debian/changelog
vendored
@@ -1,3 +1,31 @@
|
||||
i2p (0.9.5-2) UNRELEASED; urgency=low
|
||||
|
||||
* debian/i2p.postinst: Explicitly set permissions on /etc/i2p/wrapper.config
|
||||
to compensate for stricter umasks.
|
||||
|
||||
-- Kill Your TV <killyourtv@i2pmail.org> Sun, 31 Mar 2013 12:50:17 +0000
|
||||
|
||||
i2p (0.9.5-1) unstable; urgency=low
|
||||
|
||||
* New Upstream release
|
||||
* Drop debian/patches/0007-backported-news-fix.patch and
|
||||
debian/patches/#0006-remove-izpack.patch
|
||||
* Spanish debconf update
|
||||
|
||||
-- Kill Your TV <killyourtv@i2pmail.org> Fri, 08 Mar 2013 23:04:51 +0000
|
||||
|
||||
i2p (0.9.4-3) unstable; urgency=low
|
||||
|
||||
* Backport fix from MTN for ticket #817
|
||||
|
||||
-- Kill Your TV <killyourtv@i2pmail.org> Sat, 22 Dec 2012 13:07:47 +0000
|
||||
|
||||
i2p (0.9.4-2) unstable; urgency=low
|
||||
|
||||
* Add missing build-dep on libservice-wrapper-java
|
||||
|
||||
-- Kill Your TV <killyourtv@i2pmail.org> Mon, 17 Dec 2012 20:40:07 +0000
|
||||
|
||||
i2p (0.9.4-1) stable; urgency=low
|
||||
|
||||
* explicitly prefer openjdk-*-headless over default-jre-headless. Debian
|
||||
|
||||
10
debian/i2p-router.install
vendored
10
debian/i2p-router.install
vendored
@@ -18,6 +18,7 @@ pkg-temp/router.config usr/share/i2p
|
||||
pkg-temp/systray.config usr/share/i2p
|
||||
pkg-temp/webapps usr/share/i2p
|
||||
|
||||
|
||||
pkg-temp/lib/BOB.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/commons-el.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/commons-logging.jar usr/share/i2p/lib
|
||||
@@ -28,12 +29,21 @@ pkg-temp/lib/i2ptunnel.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jasper-compiler.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jasper-runtime.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/javax.servlet.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jetty-continuation.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jetty-deploy.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jetty-http.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jetty-i2p.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jetty-io.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jetty-java5-threadpool.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jetty-rewrite-handler.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jetty-security.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jetty-servlet.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jetty-servlets.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jetty-sslengine.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jetty-start.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jetty-util.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jetty-webapp.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jetty-xml.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jrobin.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/jstl.jar usr/share/i2p/lib
|
||||
pkg-temp/lib/mstreaming.jar usr/share/i2p/lib
|
||||
|
||||
1
debian/i2p.postinst
vendored
1
debian/i2p.postinst
vendored
@@ -60,6 +60,7 @@ case "$1" in
|
||||
sed -e "s/^ *wrapper\.java\.maxmemory=.*/wrapper\.java\.maxmemory=$MEMORYLIMIT/" \
|
||||
< /etc/i2p/wrapper.config > /etc/i2p/wrapper.config.tmp
|
||||
mv -f /etc/i2p/wrapper.config.tmp /etc/i2p/wrapper.config
|
||||
chmod 0644 -f /etc/i2p/wrapper.config
|
||||
|
||||
# Older versions of adduser created the home directory.
|
||||
# The version of adduser in Debian unstable does not.
|
||||
|
||||
88
history.txt
88
history.txt
@@ -1,3 +1,91 @@
|
||||
2013-04-21 str4d
|
||||
* i2ptunnel: Return "Invalid Request URI" instead of "Non-HTTP Protocol" for
|
||||
URIs with illegal characters (ticket #891)
|
||||
|
||||
2013-04-19 kytv
|
||||
* Installer: Run fixperms.bat under any version of Windows that's not XP or
|
||||
2003 to ensure it's run under Vista, 7, 8, and the upcoming "Blue"
|
||||
|
||||
2013-04-19 zzz
|
||||
* AppManager: Register jetty, console, and SAM with manager
|
||||
* i2psnark: Disable spellcheck in more form fields
|
||||
* LogManager: Add support for saving properties added in recent releases
|
||||
* Updates:
|
||||
- Notify manager about all available update methods at once, so the priority
|
||||
system works and it doesn't only update via HTTP
|
||||
- Start router update download at startup if available
|
||||
- Only check plugins when core version increases, not decreases, so we
|
||||
don't update plugins when downgrading
|
||||
- Limit length of URL shown on summary bar
|
||||
* WorkingDir: Correctly strip DOS line endings while migrating,
|
||||
to fix eepsite location on 0.9.5 Windows installs (ticket #919)
|
||||
|
||||
2013-04-18 zzz
|
||||
* i2psnark: Fix params after P-R-G
|
||||
* i2ptunnel: Set target=_top in all external links to break out of console iframe
|
||||
* SusiMail: Fix loading of Jetty classes
|
||||
|
||||
2013-04-17 zzz
|
||||
* Console: Better handling of missing font for graphing (ticket #915)
|
||||
* Eepsite: Fix jetty.xml configuration of ThreadPool
|
||||
* i2ptunnel: Fix default form action (ticket #882)
|
||||
* SusiDNS: Set target=_top in all external links to break out of console iframe
|
||||
* Systray: Better detection of 64-bit Windows (tickets #756, #912)
|
||||
|
||||
2013-04-16 zzz
|
||||
* ClientAppManager: Add method to look up clients by class and args
|
||||
* Console: Implement stopping of clients using the ClientApp interface
|
||||
(ticket #347)
|
||||
* SAM: Implement ClientApp interface (ticket #347)
|
||||
|
||||
2013-04-15 zzz
|
||||
* Console: Move from deprecated Jetty SSL methods to SslContextFactory
|
||||
* i2psnark:
|
||||
- Add data directory configuration to GUI (ticket #768)
|
||||
- Add page size configuration to GUI
|
||||
- Multiple instance DHT file cleanup
|
||||
- Mime type fixes
|
||||
- Remove web classes from jar
|
||||
|
||||
2013-04-14 zzz
|
||||
* i2psnark:
|
||||
- Set unique tunnel nickname for additional instances
|
||||
- Increase page size to 50
|
||||
* Jetty logging: Fix logging using I2PLogger class;
|
||||
log ignored messages at debug level
|
||||
|
||||
2013-04-13 zzz
|
||||
* Console: Add /graph support for bw.combined, consolidate
|
||||
rendering code (ticket #890)
|
||||
* i2psnark:
|
||||
- Limit number of torrents displayed; add previous/next page buttons
|
||||
- Only register one instance with UpdateManager
|
||||
|
||||
2013-04-10 zzz
|
||||
* Jetty: Upgrade to Jetty 7.6.10.v20130312
|
||||
- Jetty build.xml improvements
|
||||
- MigrateJetty improvements
|
||||
- Lots of changes in RouterConsoleRunner and WebApp classes
|
||||
- Lots of eepsite config file changes
|
||||
- Changes to I2PLogger and I2PRequestLog
|
||||
- Use JettyStart class to start Jetty from clients.config
|
||||
* i2psnark: Eliminate Jetty dependencies in i2psnark for good.
|
||||
Required due to webapp classloader changes in Jetty 7,
|
||||
we can no longer access or extend Jetty classes.
|
||||
- Extend javax HttpServlet instead of Jetty's DefaultServlet
|
||||
- Implement BasicServlet to replace functions of DefaultServlet
|
||||
- Add MimeTypes implementation to add to servlet's defaults
|
||||
- Add local mime.properties file, remove checks in I2PSnarkServlet for those
|
||||
we were missing
|
||||
- Eliminate all use of Jetty utility classes including MimeType, Resource,
|
||||
Buffer, Cache, URIUtil, ...
|
||||
- Use servlet path everywhere, so the war can be renamed
|
||||
- Use servlet path as base for config file and data directory names,
|
||||
so we may have multiple instances running together
|
||||
- Don't override service(), use doGet() and doPost() instead
|
||||
* I2NP: Ignore unused 7 bits of the Database Lookup Message,
|
||||
so we can use them later
|
||||
|
||||
* 2013-03-08 0.9.5 released
|
||||
|
||||
2013-03-03 zzz
|
||||
|
||||
112
installer/c/i2pExe/I2P.rc
Normal file
112
installer/c/i2pExe/I2P.rc
Normal file
@@ -0,0 +1,112 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "afxres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource1.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""afxres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,9,5,1
|
||||
PRODUCTVERSION 0,9,5,1
|
||||
FILEFLAGSMASK 0x17L
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x4L
|
||||
FILETYPE 0x1L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "Comments", "Anonymity for the masses"
|
||||
VALUE "CompanyName", "The I2P Project"
|
||||
VALUE "FileDescription", "I2P"
|
||||
VALUE "FileVersion", "0, 9, 5, 1"
|
||||
VALUE "InternalName", "I2P"
|
||||
VALUE "LegalCopyright", "Public Domain"
|
||||
VALUE "OriginalFilename", "I2P.exe"
|
||||
VALUE "ProductName", " I2P"
|
||||
VALUE "ProductVersion", "0, 9, 5, 1"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
IDI_ICON1 ICON "../../resources/start.ico"
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
||||
22
installer/c/i2pExe/I2P.sln
Normal file
22
installer/c/i2pExe/I2P.sln
Normal file
@@ -0,0 +1,22 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual Studio 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "I2P", "I2P.vcproj", "{0A74F3D1-C70A-4CF0-9390-C676025ED12F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
Release-alpha|Win32 = Release-alpha|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{0A74F3D1-C70A-4CF0-9390-C676025ED12F}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{0A74F3D1-C70A-4CF0-9390-C676025ED12F}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{0A74F3D1-C70A-4CF0-9390-C676025ED12F}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{0A74F3D1-C70A-4CF0-9390-C676025ED12F}.Release|Win32.Build.0 = Release|Win32
|
||||
{0A74F3D1-C70A-4CF0-9390-C676025ED12F}.Release-alpha|Win32.ActiveCfg = Release-alpha|Win32
|
||||
{0A74F3D1-C70A-4CF0-9390-C676025ED12F}.Release-alpha|Win32.Build.0 = Release-alpha|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
BIN
installer/c/i2pExe/I2P.suo
Normal file
BIN
installer/c/i2pExe/I2P.suo
Normal file
Binary file not shown.
327
installer/c/i2pExe/I2P.vcproj
Normal file
327
installer/c/i2pExe/I2P.vcproj
Normal file
@@ -0,0 +1,327 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9.00"
|
||||
Name="I2P"
|
||||
ProjectGUID="{0A74F3D1-C70A-4CF0-9390-C676025ED12F}"
|
||||
RootNamespace="I2P"
|
||||
Keyword="Win32Proj"
|
||||
TargetFrameworkVersion="131072"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="Debug"
|
||||
IntermediateDirectory="Debug"
|
||||
ConfigurationType="1"
|
||||
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories=""C:\Program Files\Java\jdk1.6.0\include\win32";"C:\Program Files\Java\jdk1.6.0\include""
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="Shlwapi.lib"
|
||||
OutputFile="$(OutDir)/I2P.exe"
|
||||
LinkIncremental="2"
|
||||
GenerateDebugInformation="true"
|
||||
ProgramDatabaseFile="$(OutDir)/I2P.pdb"
|
||||
SubSystem="2"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
AdditionalManifestFiles="I2P.exe.manifest"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="Release"
|
||||
IntermediateDirectory="Release"
|
||||
ConfigurationType="1"
|
||||
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
InlineFunctionExpansion="0"
|
||||
AdditionalIncludeDirectories=""C:\Program Files\Java\jdk1.6.0_13\include";"C:\Program Files\Java\jdk1.6.0_13\include\win32""
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
|
||||
RuntimeLibrary="0"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="shlwapi.lib"
|
||||
OutputFile="$(OutDir)/I2P.exe"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release-alpha|Win32"
|
||||
OutputDirectory="$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalOptions="/D"ALPHA""
|
||||
Optimization="2"
|
||||
InlineFunctionExpansion="0"
|
||||
AdditionalIncludeDirectories=""C:\Program Files\Java\jdk1.6.0\include";"C:\Program Files\Java\jdk1.6.0\include\win32""
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
|
||||
RuntimeLibrary="0"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="shlwapi.lib"
|
||||
OutputFile="$(OutDir)/I2P.exe"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\i2p.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\java.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\java_md.c"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\errors.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\java.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\java_md.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\jni.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\jni_md.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\resource.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
|
||||
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\I2P.rc"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\resources\start.ico"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
11
installer/c/i2pExe/errors.h
Normal file
11
installer/c/i2pExe/errors.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef _I2P_ERRORS_H
|
||||
#define _I2P_ERRORS_h
|
||||
|
||||
|
||||
#define ERROR_COULDNT_LOAD_JVM 6
|
||||
#define ERROR_COULDNT_PARSE_ARGUMENTS 7
|
||||
#define ERROR_COULDNT_INITIALIZE_JVM 8
|
||||
#define ERROR_STARTING_PROGRAM 9
|
||||
#define ERROR_COULDNT_FIND_JVM 10
|
||||
|
||||
#endif /* _I2P_ERRORS_H */
|
||||
255
installer/c/i2pExe/i2p.c
Normal file
255
installer/c/i2pExe/i2p.c
Normal file
@@ -0,0 +1,255 @@
|
||||
/**
|
||||
* Customized I2P launcher.
|
||||
* Launches the JRE within the process, to allow Task Manager to show
|
||||
* "I2P.exe" as the process, and firewalls to control access of
|
||||
* "I2P.exe".
|
||||
*/
|
||||
|
||||
#include "errors.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
//BOOL MoveFontPropertiesFile(const char *path);
|
||||
#ifdef _WIN32
|
||||
void SetWorkingDirectory(char *path);
|
||||
#endif
|
||||
void readOptions(char***, int*);
|
||||
//BOOL localJREExists(const char*);
|
||||
//BOOL exist(const char*);
|
||||
|
||||
// defined in java.c
|
||||
extern void* MemAlloc(size_t);
|
||||
// defined in java.c
|
||||
extern int launchJVM(int, char**);
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char** argv) {
|
||||
|
||||
int read_options_size;
|
||||
char** read_options;
|
||||
int ret = 0;
|
||||
//int current_argc = 0;
|
||||
int new_argc;
|
||||
char** new_argv;
|
||||
int i;
|
||||
#ifdef _WIN32
|
||||
char currentDirectory[MAX_PATH+1];
|
||||
#endif
|
||||
|
||||
// Set/get the correct working directory.
|
||||
#ifdef _WIN32
|
||||
SetWorkingDirectory(currentDirectory);
|
||||
#endif
|
||||
|
||||
// If there are command-line arguments, just use them
|
||||
if(argc > 1) {
|
||||
ret = launchJVM(argc, argv);
|
||||
} else {
|
||||
// Read in options from disk (launch.properties)
|
||||
// or the default ones (if no launch.properties existed)
|
||||
readOptions(&read_options, &read_options_size);
|
||||
|
||||
// Construct a new argc & argv to pass to launchJVM
|
||||
new_argc = read_options_size;
|
||||
new_argv = (char**)MemAlloc(sizeof(char*) * (new_argc+1));
|
||||
|
||||
// copy process name
|
||||
new_argv[0] = argv[0];
|
||||
// copy arguments from properties file
|
||||
for(i = 1; i <= read_options_size; i++)
|
||||
new_argv[i] = read_options[i-1];
|
||||
// copy argv arguments as arguments after the properties file
|
||||
// (generally used as arguments for I2P)
|
||||
//for(current_argc = 1; current_argc < argc; current_argc++)
|
||||
// new_argv[i++] = argv[current_argc];
|
||||
|
||||
new_argv[i] = NULL;
|
||||
|
||||
// options are no longer necessary -- free them up.
|
||||
if(read_options != 0)
|
||||
free(read_options);
|
||||
|
||||
ret = launchJVM(new_argc, new_argv);
|
||||
free(new_argv);
|
||||
}
|
||||
switch(ret) {
|
||||
case ERROR_COULDNT_FIND_JVM:
|
||||
case ERROR_COULDNT_INITIALIZE_JVM:
|
||||
case ERROR_COULDNT_LOAD_JVM:
|
||||
#ifdef _WIN32
|
||||
if (MessageBox(NULL, "I2P needs the Java Runtime Environment 5.0 or above. Click OK to go to www.java.com, where you can install Java.",
|
||||
"I2P Launcher Error",
|
||||
MB_ICONWARNING | MB_OKCANCEL) == IDOK)
|
||||
ShellExecute(NULL, NULL, "http://www.java.com/", "", "", SW_SHOWNORMAL);
|
||||
#endif
|
||||
break;
|
||||
case ERROR_COULDNT_PARSE_ARGUMENTS:
|
||||
#ifdef _WIN32
|
||||
MessageBox(NULL, "I2P failed to parse the commandline arguments to Java.\n"
|
||||
"Please download and install I2P again.",
|
||||
"I2P Launcher Error", MB_OK);
|
||||
#endif
|
||||
break;
|
||||
case ERROR_STARTING_PROGRAM:
|
||||
#ifdef _WIN32
|
||||
MessageBox(NULL, "I2P was unable to load.\n"
|
||||
"Please download and install I2P again.",
|
||||
"I2P Launcher Error", MB_OK);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define MAX_OPTION_SIZE 100
|
||||
#define MAX_OPTIONS 100
|
||||
|
||||
/**
|
||||
* Read in the launch.properties file and generate psuedo-commandline arguments.
|
||||
*/
|
||||
void readOptions(char*** options, int* size) {
|
||||
FILE* file;
|
||||
char* buffer;
|
||||
int i;
|
||||
size_t currentlen;
|
||||
char* command;
|
||||
|
||||
file = fopen("launch.properties", "r");
|
||||
if(file == NULL) {
|
||||
// default to certain values.
|
||||
*size = 9;
|
||||
|
||||
*options = (char**)MemAlloc(sizeof(char*) * (*size));
|
||||
i = 0;
|
||||
(*options)[i++] = "-Xms64m";
|
||||
(*options)[i++] = "-Xmx128m";
|
||||
//(*options)[i++] = "-Djava.net.preferIPv6Addresses=false";
|
||||
//(*options)[i++] = "-Djava.net.preferIPv4Stack=true";
|
||||
(*options)[i++] = "-Djava.library.path=.;lib";
|
||||
(*options)[i++] = "-DloggerFilenameOverride=logs/log-router-@.txt";
|
||||
(*options)[i++] = "-Dorg.mortbay.http.Version.paranoid=true";
|
||||
(*options)[i++] = "-Dorg.mortbay.util.FileResource.checkAliases=false";
|
||||
(*options)[i++] = "-cp";
|
||||
(*options)[i++] = "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:lib/i2psnark.jar:lib/jrobin.jar:lib/jstl.jar:lib/standard.jar:lib/jetty-i2p.jar:lib/jetty-java5-threadpool.jar:lib/jetty-rewrite-handler.jar:lib/jetty-sslengine.jar:lib/jetty-start.jar:lib/jetty-util.jar";
|
||||
(*options)[i++] = "net.i2p.router.RouterLaunch";
|
||||
#ifdef ALPHA
|
||||
#pragma message ("\n\n!!!!!!!!!!!!!! building ALPHA !!!!!!!!!!!!!!\n\n")
|
||||
(*options)[(*size) - 3] = "-agentlib:yjpagent=port=11111";
|
||||
(*options)[(*size) - 2] = "-jar";
|
||||
(*options)[(*size) - 1] = "I2P.jar";
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
*options = (char**)MemAlloc(sizeof(char*) * MAX_OPTIONS);
|
||||
*size = 0;
|
||||
|
||||
for(i = 0; i < MAX_OPTIONS && !feof(file); i++) {
|
||||
buffer = (char*)MemAlloc(sizeof(char) * MAX_OPTION_SIZE);
|
||||
// get the next option.
|
||||
if(fgets(buffer, MAX_OPTION_SIZE - 1, file) == NULL)
|
||||
break;
|
||||
// strip off the \n if it exists.
|
||||
currentlen = strlen(buffer);
|
||||
if(currentlen > 0 && buffer[currentlen-1] == '\n') {
|
||||
buffer[currentlen-1] = 0;
|
||||
currentlen--;
|
||||
}
|
||||
// downsize the storage for the command.
|
||||
currentlen++; // (include \0)
|
||||
command = (char*)MemAlloc(sizeof(char) * currentlen);
|
||||
strcpy(command, buffer);
|
||||
free(buffer);
|
||||
|
||||
// set the option & increase the size
|
||||
if(currentlen > 0) {
|
||||
(*options)[i] = command;
|
||||
(*size)++;
|
||||
}
|
||||
}
|
||||
|
||||
if(file != NULL)
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the current working directory to wherever I2P.exe is located
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
static void
|
||||
SetWorkingDirectory(char *path) {
|
||||
GetModuleFileName(NULL, path, MAX_PATH + 1);
|
||||
*strrchr(path, '\\') = '\0'; // remove the .exe
|
||||
SetCurrentDirectory(path);
|
||||
GetCurrentDirectory(MAX_PATH + 1, path);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Checks to see if an app-installed JRE exists.
|
||||
*/
|
||||
/*
|
||||
BOOL localJREExists(const char* path) {
|
||||
char localBuf[MAX_PATH + 1];
|
||||
sprintf(localBuf, "%s\\jre\\bin\\hotspot\\jvm.dll\0", path);
|
||||
return exist(localBuf);
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copies the windows.font.properties file to the jre so that
|
||||
* we use the proper fonts for international installs.
|
||||
*/
|
||||
/*
|
||||
BOOL
|
||||
MoveFontPropertiesFile(const char *path) {
|
||||
char curFontFile[MAX_PATH + 1];
|
||||
char newFontFile[MAX_PATH + 1];
|
||||
BOOL copySucceeded;
|
||||
sprintf(curFontFile, "%s\\windows.font.properties\0", path);
|
||||
sprintf(newFontFile, "%s\\jre\\lib\\font.properties\0", path);
|
||||
if(!exist(curFontFile))
|
||||
return TRUE;
|
||||
|
||||
copySucceeded = CopyFile(curFontFile, newFontFile, FALSE);
|
||||
if(copySucceeded)
|
||||
DeleteFile(curFontFile);
|
||||
return copySucceeded;
|
||||
}
|
||||
*/
|
||||
/*
|
||||
BOOL exist(const char *filename) {
|
||||
struct stat s;
|
||||
return stat(filename, &s) == 0 ? TRUE : FALSE;
|
||||
}
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllimport) char **__initenv;
|
||||
|
||||
int WINAPI
|
||||
WinMain(HINSTANCE inst, HINSTANCE previnst, LPSTR cmdline, int cmdshow)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* It turns out, that Windows can set thread locale to default user locale
|
||||
* instead of default system locale. We correct this by explicitely setting
|
||||
* thread locale to system default.
|
||||
*/
|
||||
SetThreadLocale(GetSystemDefaultLCID());
|
||||
|
||||
__initenv = _environ;
|
||||
ret = main(__argc, __argv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
1042
installer/c/i2pExe/java.c
Normal file
1042
installer/c/i2pExe/java.c
Normal file
File diff suppressed because it is too large
Load Diff
76
installer/c/i2pExe/java.h
Normal file
76
installer/c/i2pExe/java.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* @(#)java.h 1.23 04/04/24
|
||||
*
|
||||
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
|
||||
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*/
|
||||
|
||||
/* Backported from Tiger (1.5) java.h 1.26 04/01/12 */
|
||||
|
||||
#ifndef _JAVA_H_
|
||||
#define _JAVA_H_
|
||||
|
||||
/*
|
||||
* Get system specific defines.
|
||||
*/
|
||||
#include "jni.h"
|
||||
#include "java_md.h"
|
||||
|
||||
/*
|
||||
* Pointers to the needed JNI invocation API, initialized by LoadJavaVM.
|
||||
*/
|
||||
typedef jint (JNICALL *CreateJavaVM_t)(JavaVM **pvm, void **env, void *args);
|
||||
typedef jint (JNICALL *GetDefaultJavaVMInitArgs_t)(void *args);
|
||||
|
||||
typedef struct {
|
||||
CreateJavaVM_t CreateJavaVM;
|
||||
GetDefaultJavaVMInitArgs_t GetDefaultJavaVMInitArgs;
|
||||
} InvocationFunctions;
|
||||
|
||||
/*
|
||||
* Prototypes for launcher functions in the system specific java_md.c.
|
||||
*/
|
||||
|
||||
jboolean
|
||||
LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn);
|
||||
|
||||
jboolean
|
||||
GetApplicationHome(char *buf, jint bufsize);
|
||||
|
||||
const char *
|
||||
GetArch();
|
||||
|
||||
int CreateExecutionEnvironment(int *_argc,
|
||||
char ***_argv,
|
||||
char jrepath[],
|
||||
jint so_jrepath,
|
||||
char jvmpath[],
|
||||
jint so_jvmpath,
|
||||
char **original_argv);
|
||||
|
||||
/*
|
||||
* Report an error message to stderr or a window as appropriate. The
|
||||
* flag always is set to JNI_TRUE if message is to be reported to both
|
||||
* strerr and windows and set to JNI_FALSE if the message should only
|
||||
* be sent to a window.
|
||||
*/
|
||||
void ReportErrorMessage(char * message, jboolean always);
|
||||
void ReportErrorMessage2(char * format, char * string, jboolean always);
|
||||
|
||||
/*
|
||||
* Report an exception which terminates the vm to stderr or a window
|
||||
* as appropriate.
|
||||
*/
|
||||
void ReportExceptionDescription(JNIEnv * env);
|
||||
|
||||
jboolean RemovableMachineDependentOption(char * option);
|
||||
void PrintMachineDependentOptions();
|
||||
|
||||
/*
|
||||
* Functions defined in java.c and used in java_md.c.
|
||||
*/
|
||||
jint ReadKnownVMs(const char *jrepath, char * arch, jboolean speculative);
|
||||
char *CheckJvmType(int *argc, char ***argv, jboolean speculative);
|
||||
void* MemAlloc(size_t size);
|
||||
|
||||
#endif /* _JAVA_H_ */
|
||||
388
installer/c/i2pExe/java_md.c
Normal file
388
installer/c/i2pExe/java_md.c
Normal file
@@ -0,0 +1,388 @@
|
||||
/**
|
||||
* Derived from the shared source for the 'java' commandline tool.
|
||||
*/
|
||||
|
||||
/* Backported from Tiger (1.5) java_md.c 1.40 04/01/12 */
|
||||
/* AMD64 support removed */
|
||||
|
||||
#include <windows.h>
|
||||
#include <process.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "jni.h"
|
||||
#include "java.h"
|
||||
|
||||
#define JVM_DLL "jvm.dll"
|
||||
#define JAVA_DLL "java.dll"
|
||||
|
||||
/*
|
||||
* Prototypes.
|
||||
*/
|
||||
static jboolean GetPublicJREHome(char *path, jint pathsize);
|
||||
static jboolean GetJVMPath(const char *jrepath, const char *jvmtype,
|
||||
char *jvmpath, jint jvmpathsize);
|
||||
static jboolean GetJREPath(char *path, jint pathsize);
|
||||
|
||||
const char *
|
||||
GetArch()
|
||||
{
|
||||
#ifdef _WIN64
|
||||
return "ia64";
|
||||
#else
|
||||
return "i386";
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
int
|
||||
CreateExecutionEnvironment(int *_argc,
|
||||
char ***_argv,
|
||||
char jrepath[],
|
||||
jint so_jrepath,
|
||||
char jvmpath[],
|
||||
jint so_jvmpath,
|
||||
char **original_argv) {
|
||||
char * jvmtype;
|
||||
|
||||
/* Find out where the JRE is that we will be using. */
|
||||
if (!GetJREPath(jrepath, so_jrepath)) {
|
||||
ReportErrorMessage("Could not find a suitable Java 2 Runtime Environment.",
|
||||
JNI_TRUE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Find the specified JVM type */
|
||||
if (ReadKnownVMs(jrepath, (char*)GetArch(), JNI_FALSE) < 1) {
|
||||
ReportErrorMessage("Error: no known VMs. (check for corrupt jvm.cfg file)",
|
||||
JNI_TRUE);
|
||||
return 1;
|
||||
}
|
||||
jvmtype = CheckJvmType(_argc, _argv, JNI_FALSE);
|
||||
|
||||
jvmpath[0] = '\0';
|
||||
if (!GetJVMPath(jrepath, jvmtype, jvmpath, so_jvmpath)) {
|
||||
char * message=NULL;
|
||||
const char * format = "Error: no `%s' JVM at `%s'.";
|
||||
message = (char *)MemAlloc((strlen(format)+strlen(jvmtype)+
|
||||
strlen(jvmpath)) * sizeof(char));
|
||||
sprintf(message,format, jvmtype, jvmpath);
|
||||
ReportErrorMessage(message, JNI_TRUE);
|
||||
return 1;
|
||||
}
|
||||
/* If we got here, jvmpath has been correctly initialized. */
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Find path to JRE based on .exe's location or registry settings.
|
||||
*/
|
||||
jboolean
|
||||
GetJREPath(char *path, jint pathsize)
|
||||
{
|
||||
char javadll[MAXPATHLEN];
|
||||
struct stat s;
|
||||
//
|
||||
// if (GetApplicationHome(path, pathsize)) {
|
||||
// /* Is JRE co-located with the application? */
|
||||
// sprintf(javadll, "%s\\bin\\" JAVA_DLL, path);
|
||||
// if (stat(javadll, &s) == 0) {
|
||||
// goto found;
|
||||
// }
|
||||
|
||||
// /* Does this app ship a private JRE in <apphome>\jre directory? */
|
||||
// sprintf(javadll, "%s\\jre\\bin\\" JAVA_DLL, path);
|
||||
// if (stat(javadll, &s) == 0) {
|
||||
// strcat(path, "\\jre");
|
||||
// goto found;
|
||||
// }
|
||||
// }
|
||||
|
||||
/* Look for a public JRE on this machine. */
|
||||
if (GetPublicJREHome(path, pathsize))
|
||||
goto found;
|
||||
|
||||
fprintf(stderr, "Error: could not find " JAVA_DLL "\n");
|
||||
return JNI_FALSE;
|
||||
|
||||
found:
|
||||
|
||||
{
|
||||
// trick into thinking that the jre/bin path is in the PATH
|
||||
char *oldPath = getenv("PATH");
|
||||
char *newPath;
|
||||
if(oldPath != NULL) {
|
||||
newPath = MemAlloc(strlen(oldPath) + strlen(path) + 6 + 6 + 1); // PATH=<new>\bin;<old>
|
||||
sprintf(newPath, "PATH=%s\\bin;%s", path, oldPath);
|
||||
} else {
|
||||
newPath = MemAlloc(strlen(path) + 6 + 1);
|
||||
sprintf(newPath, "PATH=%s\\bin", path);
|
||||
}
|
||||
_putenv(newPath);
|
||||
free(newPath);
|
||||
}
|
||||
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a JRE location and a JVM type, construct what the name the
|
||||
* JVM shared library will be. Return true, if such a library
|
||||
* exists, false otherwise.
|
||||
*/
|
||||
static jboolean
|
||||
GetJVMPath(const char *jrepath, const char *jvmtype,
|
||||
char *jvmpath, jint jvmpathsize)
|
||||
{
|
||||
struct stat s;
|
||||
if (strchr(jvmtype, '/') || strchr(jvmtype, '\\')) {
|
||||
sprintf(jvmpath, "%s\\" JVM_DLL, jvmtype);
|
||||
} else {
|
||||
sprintf(jvmpath, "%s\\bin\\%s\\" JVM_DLL, jrepath, jvmtype);
|
||||
}
|
||||
if (stat(jvmpath, &s) == 0) {
|
||||
return JNI_TRUE;
|
||||
} else {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Load a jvm from "jvmpath" and initialize the invocation functions.
|
||||
*/
|
||||
jboolean
|
||||
LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn)
|
||||
{
|
||||
HINSTANCE handle;
|
||||
|
||||
/* Load the Java VM DLL */
|
||||
if ((handle = LoadLibrary(jvmpath)) == 0) {
|
||||
ReportErrorMessage2("Error loading: %s", (char *)jvmpath, JNI_TRUE);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
/* Now get the function addresses */
|
||||
ifn->CreateJavaVM =
|
||||
(void *)GetProcAddress(handle, "JNI_CreateJavaVM");
|
||||
ifn->GetDefaultJavaVMInitArgs =
|
||||
(void *)GetProcAddress(handle, "JNI_GetDefaultJavaVMInitArgs");
|
||||
if (ifn->CreateJavaVM == 0 || ifn->GetDefaultJavaVMInitArgs == 0) {
|
||||
ReportErrorMessage2("Error: can't find JNI interfaces in: %s",
|
||||
(char *)jvmpath, JNI_TRUE);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* If app is "c:\foo\bin\javac", then put "c:\foo" into buf.
|
||||
*/
|
||||
jboolean
|
||||
GetApplicationHome(char *buf, jint bufsize)
|
||||
{
|
||||
char *cp;
|
||||
GetModuleFileName(0, buf, bufsize);
|
||||
*strrchr(buf, '\\') = '\0'; /* remove .exe file name */
|
||||
if ((cp = strrchr(buf, '\\')) == 0) {
|
||||
/* This happens if the application is in a drive root, and
|
||||
* there is no bin directory. */
|
||||
buf[0] = '\0';
|
||||
return JNI_FALSE;
|
||||
}
|
||||
if(strcmp(cp, "\\bin") == 0)
|
||||
*cp = '\0'; /* remove the bin\ part */
|
||||
// else c:\foo\something\javac
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helpers to look in the registry for a public JRE.
|
||||
*/
|
||||
/* Same for 1.5.0, 1.5.1, 1.5.2 etc. */
|
||||
#define DOTRELEASE JDK_MAJOR_VERSION "." JDK_MINOR_VERSION
|
||||
#define JRE_KEY "Software\\JavaSoft\\Java Runtime Environment"
|
||||
|
||||
static jboolean
|
||||
GetStringFromRegistry(HKEY key, const char *name, char *buf, jint bufsize)
|
||||
{
|
||||
DWORD type, size;
|
||||
|
||||
if (RegQueryValueEx(key, name, 0, &type, 0, &size) == 0
|
||||
&& type == REG_SZ
|
||||
&& (size < (unsigned int)bufsize)) {
|
||||
if (RegQueryValueEx(key, name, 0, 0, buf, &size) == 0) {
|
||||
return JNI_TRUE;
|
||||
}
|
||||
}
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
static BOOL versionOk(char *version)
|
||||
{
|
||||
char *major;
|
||||
int major_version;
|
||||
int minor_version;
|
||||
char *minor_start;
|
||||
// 1. find the .
|
||||
minor_start = strstr(version,".");
|
||||
if (minor_start == NULL) // no . found.
|
||||
return FALSE;
|
||||
|
||||
major = (char *)malloc(minor_start - version + 1);
|
||||
strncpy(major,version,minor_start - version);
|
||||
major[minor_start - version]='\0';
|
||||
major_version = atoi(major);
|
||||
free(major);
|
||||
|
||||
if (minor_start[1] == '\0') // "x." ?
|
||||
return FALSE;
|
||||
|
||||
// anything less than 1.6 is not supported
|
||||
minor_version = atoi(++minor_start);
|
||||
if (major_version <1 || (major_version < 2 && minor_version < 6))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static jboolean
|
||||
GetPublicJREHome(char *buf, jint bufsize)
|
||||
{
|
||||
HKEY key, subkey;
|
||||
char version[MAXPATHLEN];
|
||||
|
||||
/* Find the current version of the JRE */
|
||||
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, JRE_KEY, 0, KEY_READ, &key) != 0) {
|
||||
fprintf(stderr, "Error opening registry key '" JRE_KEY "'\n");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
if (!GetStringFromRegistry(key, "CurrentVersion",
|
||||
version, sizeof(version))) {
|
||||
fprintf(stderr, "Failed reading value of registry key:\n\t"
|
||||
JRE_KEY "\\CurrentVersion\n");
|
||||
RegCloseKey(key);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
/* Check if the version is good */
|
||||
if (!versionOk(version))
|
||||
{
|
||||
RegCloseKey(key);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
/* Find directory where the current version is installed. */
|
||||
if (RegOpenKeyEx(key, version, 0, KEY_READ, &subkey) != 0) {
|
||||
fprintf(stderr, "Error opening registry key '"
|
||||
JRE_KEY "\\%s'\n", version);
|
||||
RegCloseKey(key);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
if (!GetStringFromRegistry(subkey, "JavaHome", buf, bufsize)) {
|
||||
fprintf(stderr, "Failed reading value of registry key:\n\t"
|
||||
JRE_KEY "\\%s\\JavaHome\n", version);
|
||||
RegCloseKey(key);
|
||||
RegCloseKey(subkey);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
RegCloseKey(key);
|
||||
RegCloseKey(subkey);
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
|
||||
void ReportErrorMessage(char * message, jboolean always) {
|
||||
if (message != NULL) {
|
||||
// MessageBox(NULL, message, "Java Virtual Machine Launcher",
|
||||
// (MB_OK|MB_ICONSTOP|MB_APPLMODAL));
|
||||
}
|
||||
}
|
||||
|
||||
void ReportErrorMessage2(char * format, char * string, jboolean always) {
|
||||
/*
|
||||
* The format argument must be a printf format string with one %s
|
||||
* argument, which is passed the string argument.
|
||||
*/
|
||||
size_t size;
|
||||
char * message;
|
||||
size = strlen(format) + strlen(string);
|
||||
message = (char*)MemAlloc(size*sizeof(char));
|
||||
sprintf(message, (const char *)format, string);
|
||||
|
||||
if (message != NULL) {
|
||||
// MessageBox(NULL, message, "Java Virtual Machine Launcher",
|
||||
// (MB_OK|MB_ICONSTOP|MB_APPLMODAL));
|
||||
}
|
||||
}
|
||||
|
||||
void ReportExceptionDescription(JNIEnv * env) {
|
||||
/*
|
||||
* This code should be replaced by code which opens a window with
|
||||
* the exception detail message.
|
||||
*/
|
||||
(*env)->ExceptionDescribe(env);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return JNI_TRUE for an option string that has no effect but should
|
||||
* _not_ be passed on to the vm; return JNI_FALSE otherwise. On
|
||||
* windows, there are no options that should be screened in this
|
||||
* manner.
|
||||
*/
|
||||
jboolean RemovableMachineDependentOption(char * option) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
void PrintMachineDependentOptions() {
|
||||
return;
|
||||
}
|
||||
|
||||
jboolean
|
||||
ServerClassMachine() {
|
||||
jboolean result = JNI_FALSE;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Wrapper for platform dependent unsetenv function.
|
||||
*/
|
||||
int
|
||||
UnsetEnv(char *name)
|
||||
{
|
||||
int ret;
|
||||
char *buf = MemAlloc(strlen(name) + 2);
|
||||
buf = strcat(strcpy(buf, name), "=");
|
||||
ret = _putenv(buf);
|
||||
free(buf);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* The following just map the common UNIX name to the Windows API name,
|
||||
* so that the common UNIX name can be used in shared code.
|
||||
*/
|
||||
int
|
||||
strcasecmp(const char *s1, const char *s2)
|
||||
{
|
||||
return (stricmp(s1, s2));
|
||||
}
|
||||
|
||||
int
|
||||
strncasecmp(const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
return (strnicmp(s1, s2, n));
|
||||
}
|
||||
28
installer/c/i2pExe/java_md.h
Normal file
28
installer/c/i2pExe/java_md.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* @(#)java_md.h 1.10 04/04/24
|
||||
*
|
||||
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
|
||||
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*/
|
||||
|
||||
/* Backported from Tiger (1.5) java_md.h 1.13 04/01/12 */
|
||||
|
||||
#ifndef JAVA_MD_H
|
||||
#define JAVA_MD_H
|
||||
|
||||
#include "jni.h"
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
|
||||
#define PATH_SEPARATOR ';'
|
||||
#define FILESEP "\\"
|
||||
#define FILE_SEPARATOR '\\'
|
||||
#define MAXPATHLEN MAX_PATH
|
||||
#define MAXNAMELEN MAX_PATH
|
||||
|
||||
|
||||
int UnsetEnv(char *name);
|
||||
int strcasecmp(const char *s1, const char *s2);
|
||||
int strncasecmp(const char *s1, const char *s2, size_t n);
|
||||
|
||||
#endif
|
||||
1944
installer/c/i2pExe/jni.h
Normal file
1944
installer/c/i2pExe/jni.h
Normal file
File diff suppressed because it is too large
Load Diff
19
installer/c/i2pExe/jni_md.h
Normal file
19
installer/c/i2pExe/jni_md.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* @(#)jni_md.h 1.15 05/11/17
|
||||
*
|
||||
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
||||
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _JAVASOFT_JNI_MD_H_
|
||||
#define _JAVASOFT_JNI_MD_H_
|
||||
|
||||
#define JNIEXPORT __declspec(dllexport)
|
||||
#define JNIIMPORT __declspec(dllimport)
|
||||
#define JNICALL __stdcall
|
||||
|
||||
typedef long jint;
|
||||
typedef __int64 jlong;
|
||||
typedef signed char jbyte;
|
||||
|
||||
#endif /* !_JAVASOFT_JNI_MD_H_ */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user