merge of '3b6c4d6af6fae83cc9b7d42e8515804ae48ec675'

and '799a00a4929a59478c534a56cce350cdb9a042e0'
This commit is contained in:
z3d
2010-11-23 13:54:18 +00:00
51 changed files with 998 additions and 303 deletions

View File

@ -68,6 +68,10 @@ Public domain except as listed below:
Copyright (C) 2001, 2007 Free Software Foundation, Inc. Copyright (C) 2001, 2007 Free Software Foundation, Inc.
See licenses/LICENSE-LGPLv2.1.txt See licenses/LICENSE-LGPLv2.1.txt
SSLEepGet:
Contains some code Copyright 2006 Sun Microsystems, Inc.
See licenses/LICENSE-InstallCert.txt
Router: Router:
Public domain except as listed below: Public domain except as listed below:

View File

@ -49,6 +49,8 @@ import net.i2p.util.SecureFileOutputStream;
*/ */
class ConfigParser { class ConfigParser {
private static final boolean isWindows = System.getProperty("os.name").startsWith("Win");
/** /**
* Strip the comments from a String. Lines that begin with '#' and ';' are * Strip the comments from a String. Lines that begin with '#' and ';' are
* considered comments, as well as any part of a line after a '#'. * considered comments, as well as any part of a line after a '#'.
@ -276,7 +278,8 @@ class ConfigParser {
* Write contents of Map map to the File file. Output is written * Write contents of Map map to the File file. Output is written
* with one key, value pair on each line, in the format: key=value. * with one key, value pair on each line, in the format: key=value.
* Write to a temp file in the same directory and then rename, to not corrupt * Write to a temp file in the same directory and then rename, to not corrupt
* simultaneous accesses by the router. * simultaneous accesses by the router. Except on Windows where renameTo()
* will fail if the target exists.
* *
* @param map * @param map
* A Map to write to file. * A Map to write to file.
@ -286,14 +289,19 @@ class ConfigParser {
* if file cannot be written to. * if file cannot be written to.
*/ */
public static void write(Map map, File file) throws IOException { public static void write(Map map, File file) throws IOException {
File tmp = SecureFile.createTempFile("hoststxt-", ".tmp", file.getAbsoluteFile().getParentFile()); boolean success = false;
ConfigParser if (!isWindows) {
File tmp = SecureFile.createTempFile("temp-", ".tmp", file.getAbsoluteFile().getParentFile());
ConfigParser
.write(map, new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(tmp), "UTF-8"))); .write(map, new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(tmp), "UTF-8")));
boolean success = tmp.renameTo(file); success = tmp.renameTo(file);
if (!success) {
tmp.delete();
//System.out.println("Warning: addressbook rename fail from " + tmp + " to " + file);
}
}
if (!success) { if (!success) {
// hmm, that didn't work, try it the old way // hmm, that didn't work, try it the old way
System.out.println("Warning: addressbook rename fail from " + tmp + " to " + file);
tmp.delete();
ConfigParser ConfigParser
.write(map, new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "UTF-8"))); .write(map, new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "UTF-8")));
} }

View File

@ -0,0 +1,14 @@
package org.klomp.snark;
/**
* Callback used to fetch data
* @since 0.8.2
*/
interface DataLoader
{
/**
* This is the callback that PeerConnectionOut calls to get the data from disk
* @return bytes or null for errors
*/
public byte[] loadData(int piece, int begin, int length);
}

View File

@ -39,23 +39,28 @@ class Message
final static byte REQUEST = 6; final static byte REQUEST = 6;
final static byte PIECE = 7; final static byte PIECE = 7;
final static byte CANCEL = 8; final static byte CANCEL = 8;
final static byte EXTENSION = 20;
// Not all fields are used for every message. // Not all fields are used for every message.
// KEEP_ALIVE doesn't have a real wire representation // KEEP_ALIVE doesn't have a real wire representation
byte type; byte type;
// Used for HAVE, REQUEST, PIECE and CANCEL messages. // Used for HAVE, REQUEST, PIECE and CANCEL messages.
// low byte used for EXTENSION message
int piece; int piece;
// Used for REQUEST, PIECE and CANCEL messages. // Used for REQUEST, PIECE and CANCEL messages.
int begin; int begin;
int length; int length;
// Used for PIECE and BITFIELD messages // Used for PIECE and BITFIELD and EXTENSION messages
byte[] data; byte[] data;
int off; int off;
int len; int len;
// Used to do deferred fetch of data
DataLoader dataLoader;
SimpleTimer.TimedEvent expireEvent; SimpleTimer.TimedEvent expireEvent;
/** Utility method for sending a message through a DataStream. */ /** Utility method for sending a message through a DataStream. */
@ -68,6 +73,13 @@ class Message
return; return;
} }
// Get deferred data
if (data == null && dataLoader != null) {
data = dataLoader.loadData(piece, begin, length);
if (data == null)
return; // hmm will get retried, but shouldn't happen
}
// Calculate the total length in bytes // Calculate the total length in bytes
// Type is one byte. // Type is one byte.
@ -85,8 +97,12 @@ class Message
if (type == REQUEST || type == CANCEL) if (type == REQUEST || type == CANCEL)
datalen += 4; datalen += 4;
// length is 1 byte
if (type == EXTENSION)
datalen += 1;
// add length of data for piece or bitfield array. // add length of data for piece or bitfield array.
if (type == BITFIELD || type == PIECE) if (type == BITFIELD || type == PIECE || type == EXTENSION)
datalen += len; datalen += len;
// Send length // Send length
@ -105,8 +121,11 @@ class Message
if (type == REQUEST || type == CANCEL) if (type == REQUEST || type == CANCEL)
dos.writeInt(length); dos.writeInt(length);
if (type == EXTENSION)
dos.writeByte((byte) piece & 0xff);
// Send actual data // Send actual data
if (type == BITFIELD || type == PIECE) if (type == BITFIELD || type == PIECE || type == EXTENSION)
dos.write(data, off, len); dos.write(data, off, len);
} }
@ -135,6 +154,8 @@ class Message
return "PIECE(" + piece + "," + begin + "," + length + ")"; return "PIECE(" + piece + "," + begin + "," + length + ")";
case CANCEL: case CANCEL:
return "CANCEL(" + piece + "," + begin + "," + length + ")"; return "CANCEL(" + piece + "," + begin + "," + length + ")";
case EXTENSION:
return "EXTENSION(" + piece + ',' + data.length + ')';
default: default:
return "<UNKNOWN>"; return "<UNKNOWN>";
} }

View File

@ -59,6 +59,11 @@ public class Peer implements Comparable
private long uploaded_old[] = {-1,-1,-1}; private long uploaded_old[] = {-1,-1,-1};
private long downloaded_old[] = {-1,-1,-1}; private long downloaded_old[] = {-1,-1,-1};
// bytes per bt spec: 0011223344556677
static final long OPTION_EXTENSION = 0x0000000000100000l;
static final long OPTION_FAST = 0x0000000000000004l;
private long options;
/** /**
* Creates a disconnected peer given a PeerID, your own id and the * Creates a disconnected peer given a PeerID, your own id and the
* relevant MetaInfo. * relevant MetaInfo.
@ -285,9 +290,8 @@ public class Peer implements Comparable
// Handshake write - header // Handshake write - header
dout.write(19); dout.write(19);
dout.write("BitTorrent protocol".getBytes("UTF-8")); dout.write("BitTorrent protocol".getBytes("UTF-8"));
// Handshake write - zeros // Handshake write - options
byte[] zeros = new byte[8]; dout.writeLong(OPTION_EXTENSION);
dout.write(zeros);
// Handshake write - metainfo hash // Handshake write - metainfo hash
byte[] shared_hash = metainfo.getInfoHash(); byte[] shared_hash = metainfo.getInfoHash();
dout.write(shared_hash); dout.write(shared_hash);
@ -312,8 +316,8 @@ public class Peer implements Comparable
+ "'Bittorrent protocol', got '" + "'Bittorrent protocol', got '"
+ bittorrentProtocol + "'"); + bittorrentProtocol + "'");
// Handshake read - zeros // Handshake read - options
din.readFully(zeros); options = din.readLong();
// Handshake read - metainfo hash // Handshake read - metainfo hash
bs = new byte[20]; bs = new byte[20];
@ -325,6 +329,15 @@ public class Peer implements Comparable
din.readFully(bs); din.readFully(bs);
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Read the remote side's hash and peerID fully from " + toString()); _log.debug("Read the remote side's hash and peerID fully from " + toString());
// if ((options & OPTION_EXTENSION) != 0) {
if (options != 0) {
// send them something
if (_log.shouldLog(Log.DEBUG))
//_log.debug("Peer supports extension message, what should we say? " + toString());
_log.debug("Peer supports options 0x" + Long.toString(options, 16) + ", what should we say? " + toString());
}
return bs; return bs;
} }

View File

@ -171,6 +171,13 @@ class PeerConnectionIn implements Runnable
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Received cancel(" + piece + "," + begin + ") from " + peer + " on " + peer.metainfo.getName()); _log.debug("Received cancel(" + piece + "," + begin + ") from " + peer + " on " + peer.metainfo.getName());
break; break;
case 20: // Extension message
int id = din.readUnsignedByte();
byte[] payload = new byte[i-2];
din.readFully(payload);
ps.extensionMessage(id, payload);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received extension message from " + peer + " on " + peer.metainfo.getName());
default: default:
byte[] bs = new byte[i-1]; byte[] bs = new byte[i-1];
din.readFully(bs); din.readFully(bs);

View File

@ -430,6 +430,33 @@ class PeerConnectionOut implements Runnable
return total; return total;
} }
/** @since 0.8.2 */
void sendPiece(int piece, int begin, int length, DataLoader loader)
{
boolean sendNow = false;
// are there any cases where we should?
if (sendNow) {
// queue the real thing
byte[] bytes = loader.loadData(piece, begin, length);
if (bytes != null)
sendPiece(piece, begin, length, bytes);
return;
}
// queue a fake message... set everything up,
// except save the PeerState instead of the bytes.
Message m = new Message();
m.type = Message.PIECE;
m.piece = piece;
m.begin = begin;
m.length = length;
m.dataLoader = loader;
m.off = 0;
m.len = length;
addMessage(m);
}
void sendPiece(int piece, int begin, int length, byte[] bytes) void sendPiece(int piece, int begin, int length, byte[] bytes)
{ {
Message m = new Message(); Message m = new Message();
@ -488,4 +515,16 @@ class PeerConnectionOut implements Runnable
} }
} }
} }
/** @since 0.8.2 */
void sendExtension(int id, byte[] bytes) {
Message m = new Message();
m.type = Message.EXTENSION;
m.piece = id;
m.data = bytes;
m.begin = 0;
m.length = bytes.length;
addMessage(m);
}
} }

View File

@ -20,14 +20,20 @@
package org.klomp.snark; package org.klomp.snark;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.util.Log; import net.i2p.util.Log;
class PeerState import org.klomp.snark.bencode.BDecoder;
import org.klomp.snark.bencode.BEValue;
class PeerState implements DataLoader
{ {
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(PeerState.class); private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(PeerState.class);
final Peer peer; final Peer peer;
@ -201,13 +207,28 @@ class PeerState
return; return;
} }
if (_log.shouldLog(Log.INFO))
_log.info("Queueing (" + piece + ", " + begin + ", "
+ length + ")" + " to " + peer);
// don't load the data into mem now, let PeerConnectionOut do it
out.sendPiece(piece, begin, length, this);
}
/**
* This is the callback that PeerConnectionOut calls
*
* @return bytes or null for errors
* @since 0.8.2
*/
public byte[] loadData(int piece, int begin, int length) {
byte[] pieceBytes = listener.gotRequest(peer, piece, begin, length); byte[] pieceBytes = listener.gotRequest(peer, piece, begin, length);
if (pieceBytes == null) if (pieceBytes == null)
{ {
// XXX - Protocol error-> diconnect? // XXX - Protocol error-> diconnect?
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN))
_log.warn("Got request for unknown piece: " + piece); _log.warn("Got request for unknown piece: " + piece);
return; return null;
} }
// More sanity checks // More sanity checks
@ -219,13 +240,13 @@ class PeerState
+ ", " + begin + ", " + begin
+ ", " + length + ", " + length
+ "' message from " + peer); + "' message from " + peer);
return; return null;
} }
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO))
_log.info("Sending (" + piece + ", " + begin + ", " _log.info("Sending (" + piece + ", " + begin + ", "
+ length + ")" + " to " + peer); + length + ")" + " to " + peer);
out.sendPiece(piece, begin, length, pieceBytes); return pieceBytes;
} }
/** /**
@ -413,6 +434,24 @@ class PeerState
out.cancelRequest(piece, begin, length); out.cancelRequest(piece, begin, length);
} }
/** @since 0.8.2 */
void extensionMessage(int id, byte[] bs)
{
if (id == 0) {
InputStream is = new ByteArrayInputStream(bs);
try {
BDecoder dec = new BDecoder(is);
BEValue bev = dec.bdecodeMap();
Map map = bev.getMap();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Got extension handshake message " + bev.toString());
} catch (Exception e) {}
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Got extended message type: " + id + " length: " + bs.length);
}
}
void unknownMessage(int type, byte[] bs) void unknownMessage(int type, byte[] bs)
{ {
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN))

View File

@ -242,6 +242,13 @@ public class I2PSnarkServlet extends Default {
List snarks = getSortedSnarks(req); List snarks = getSortedSnarks(req);
String uri = req.getRequestURI(); String uri = req.getRequestURI();
boolean isForm = _manager.util().connected() || !snarks.isEmpty();
if (isForm) {
out.write("<form action=\"");
out.write(uri);
out.write("\" method=\"POST\">\n");
out.write("<input type=\"hidden\" name=\"nonce\" value=\"" + _nonce + "\" >\n");
}
out.write(TABLE_HEADER); out.write(TABLE_HEADER);
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/status.png\""); out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/status.png\"");
out.write(" title=\""); out.write(" title=\"");
@ -301,25 +308,17 @@ public class I2PSnarkServlet extends Default {
out.write("</th>\n"); out.write("</th>\n");
out.write("<th align=\"center\">"); out.write("<th align=\"center\">");
if (_manager.util().connected()) { if (_manager.util().connected()) {
out.write("<a href=\"" + uri + "?action=StopAll&nonce=" + _nonce + out.write("<input type=\"image\" name=\"action\" value=\"StopAll\" title=\"");
"\" title=\"");
out.write(_("Stop all torrents and the I2P tunnel")); out.write(_("Stop all torrents and the I2P tunnel"));
out.write("\">"); out.write("\" src=\"/themes/snark/ubergine/images/stop_all.png\" alt=\"");
out.write("<img src=\"/themes/snark/ubergine/images/stop_all.png\" title=\"");
out.write(_("Stop all torrents and the I2P tunnel"));
out.write("\" alt=\"");
out.write(_("Stop All")); out.write(_("Stop All"));
out.write("\">"); out.write("\">");
out.write("</a>");
} else if (!snarks.isEmpty()) { } else if (!snarks.isEmpty()) {
out.write("<a href=\"" + uri + "?action=StartAll&nonce=" + _nonce + out.write("<input type=\"image\" name=\"action\" value=\"StartAll\" title=\"");
"\" title=\"");
out.write(_("Start all torrents and the I2P tunnel")); out.write(_("Start all torrents and the I2P tunnel"));
out.write("\" src=\"/themes/snark/ubergine/images/start_all.png\" alt=\"");
out.write(_("Start All"));
out.write("\">"); out.write("\">");
out.write("<img src=\"/themes/snark/ubergine/images/start_all.png\" title=\"");
out.write(_("Start all torrents and the I2P tunnel"));
out.write("\" alt=\"Start All\">");
out.write("</a>");
} else { } else {
out.write("&nbsp;"); out.write("&nbsp;");
} }
@ -357,6 +356,8 @@ public class I2PSnarkServlet extends Default {
} }
out.write("</table>"); out.write("</table>");
if (isForm)
out.write("</form>\n");
} }
/** /**
@ -366,7 +367,11 @@ public class I2PSnarkServlet extends Default {
String action = req.getParameter("action"); String action = req.getParameter("action");
if (action == null) { if (action == null) {
// noop // noop
} else if ("Add".equals(action)) { return;
}
if (!"POST".equals(req.getMethod()))
return;
if ("Add".equals(action)) {
String newFile = req.getParameter("newFile"); String newFile = req.getParameter("newFile");
String newURL = req.getParameter("newURL"); String newURL = req.getParameter("newURL");
// NOTE - newFile currently disabled in HTML form - see below // NOTE - newFile currently disabled in HTML form - see below
@ -410,8 +415,8 @@ public class I2PSnarkServlet extends Default {
} else { } else {
// no file or URL specified // no file or URL specified
} }
} else if ("Stop".equals(action)) { } else if (action.startsWith("Stop_")) {
String torrent = req.getParameter("torrent"); String torrent = action.substring(5);
if (torrent != null) { if (torrent != null) {
byte infoHash[] = Base64.decode(torrent); byte infoHash[] = Base64.decode(torrent);
if ( (infoHash != null) && (infoHash.length == 20) ) { // valid sha1 if ( (infoHash != null) && (infoHash.length == 20) ) { // valid sha1
@ -425,8 +430,8 @@ public class I2PSnarkServlet extends Default {
} }
} }
} }
} else if ("Start".equals(action)) { } else if (action.startsWith("Start_")) {
String torrent = req.getParameter("torrent"); String torrent = action.substring(6);
if (torrent != null) { if (torrent != null) {
byte infoHash[] = Base64.decode(torrent); byte infoHash[] = Base64.decode(torrent);
if ( (infoHash != null) && (infoHash.length == 20) ) { // valid sha1 if ( (infoHash != null) && (infoHash.length == 20) ) { // valid sha1
@ -441,8 +446,8 @@ public class I2PSnarkServlet extends Default {
} }
} }
} }
} else if ("Remove".equals(action)) { } else if (action.startsWith("Remove_")) {
String torrent = req.getParameter("torrent"); String torrent = action.substring(7);
if (torrent != null) { if (torrent != null) {
byte infoHash[] = Base64.decode(torrent); byte infoHash[] = Base64.decode(torrent);
if ( (infoHash != null) && (infoHash.length == 20) ) { // valid sha1 if ( (infoHash != null) && (infoHash.length == 20) ) { // valid sha1
@ -461,8 +466,8 @@ public class I2PSnarkServlet extends Default {
} }
} }
} }
} else if ("Delete".equals(action)) { } else if (action.startsWith("Delete_")) {
String torrent = req.getParameter("torrent"); String torrent = action.substring(7);
if (torrent != null) { if (torrent != null) {
byte infoHash[] = Base64.decode(torrent); byte infoHash[] = Base64.decode(torrent);
if ( (infoHash != null) && (infoHash.length == 20) ) { // valid sha1 if ( (infoHash != null) && (infoHash.length == 20) ) { // valid sha1
@ -618,10 +623,10 @@ public class I2PSnarkServlet extends Default {
if (r.startsWith(skip)) if (r.startsWith(skip))
r = r.substring(skip.length()); r = r.substring(skip.length());
String llc = l.toLowerCase(); String llc = l.toLowerCase();
if (llc.startsWith("the ") || llc.startsWith("the.")) if (llc.startsWith("the ") || llc.startsWith("the.") || llc.startsWith("the_"))
l = l.substring(4); l = l.substring(4);
String rlc = r.toLowerCase(); String rlc = r.toLowerCase();
if (rlc.startsWith("the ") || rlc.startsWith("the.")) if (rlc.startsWith("the ") || rlc.startsWith("the.") || rlc.startsWith("the_"))
r = r.substring(4); r = r.substring(4);
return collator.compare(l, r); return collator.compare(l, r);
} }
@ -828,62 +833,55 @@ public class I2PSnarkServlet extends Default {
out.write("</td>\n\t"); out.write("</td>\n\t");
out.write("<td align=\"center\" class=\"snarkTorrentAction " + rowClass + "\">"); out.write("<td align=\"center\" class=\"snarkTorrentAction " + rowClass + "\">");
String parameters = "&nonce=" + _nonce + "&torrent=" + Base64.encode(snark.meta.getInfoHash()); String parameters = "&nonce=" + _nonce + "&torrent=" + Base64.encode(snark.meta.getInfoHash());
String b64 = Base64.encode(snark.meta.getInfoHash());
if (showPeers) if (showPeers)
parameters = parameters + "&p=1"; parameters = parameters + "&p=1";
if (isRunning) { if (isRunning) {
out.write("<a href=\"" + uri + "?action=Stop" + parameters out.write("<input type=\"image\" name=\"action\" value=\"Stop_");
+ "\" title=\""); out.write(b64);
out.write("\" title=\"");
out.write(_("Stop the torrent")); out.write(_("Stop the torrent"));
out.write("\">"); out.write("\" src=\"/themes/snark/ubergine/images/stop.png\" alt=\"");
out.write("<img src=\"/themes/snark/ubergine/images/stop.png\" title=\"");
out.write(_("Stop the torrent"));
out.write("\" alt=\"");
out.write(_("Stop")); out.write(_("Stop"));
out.write("\">"); out.write("\">");
out.write("</a>");
} else { } else {
if (isValid) { if (isValid) {
out.write("<a href=\"" + uri + "?action=Start" + parameters out.write("<input type=\"image\" name=\"action\" value=\"Start_");
+ "\" title=\""); out.write(b64);
out.write("\" title=\"");
out.write(_("Start the torrent")); out.write(_("Start the torrent"));
out.write("\">"); out.write("\" src=\"/themes/snark/ubergine/images/start.png\" alt=\"");
out.write("<img src=\"/themes/snark/ubergine/images/start.png\" title=\"");
out.write(_("Start the torrent"));
out.write("\" alt=\"");
out.write(_("Start")); out.write(_("Start"));
out.write("\">"); out.write("\">");
out.write("</a>");
} }
out.write("<a href=\"" + uri + "?action=Remove" + parameters
+ "\" title=\""); out.write("<input type=\"image\" name=\"action\" value=\"Remove_");
out.write(b64);
out.write("\" title=\"");
out.write(_("Remove the torrent from the active list, deleting the .torrent file")); out.write(_("Remove the torrent from the active list, deleting the .torrent file"));
out.write("\" onclick=\"if (!confirm('"); out.write("\" onclick=\"if (!confirm('");
// Can't figure out how to escape double quotes inside the onclick string. // Can't figure out how to escape double quotes inside the onclick string.
// Single quotes in translate strings with parameters must be doubled. // Single quotes in translate strings with parameters must be doubled.
// Then the remaining single quite must be escaped // Then the remaining single quite must be escaped
out.write(_("Are you sure you want to delete the file \\''{0}.torrent\\'' (downloaded data will not be deleted) ?", fullFilename)); out.write(_("Are you sure you want to delete the file \\''{0}.torrent\\'' (downloaded data will not be deleted) ?", fullFilename));
out.write("')) { return false; }\">"); out.write("')) { return false; }\"");
out.write("<img src=\"/themes/snark/ubergine/images/remove.png\" title=\""); out.write(" src=\"/themes/snark/ubergine/images/remove.png\" alt=\"");
out.write(_("Remove the torrent from the active list, deleting the .torrent file"));
out.write("\" alt=\"");
out.write(_("Remove")); out.write(_("Remove"));
out.write("\">"); out.write("\">");
out.write("</a>");
out.write("<a href=\"" + uri + "?action=Delete" + parameters out.write("<input type=\"image\" name=\"action\" value=\"Delete_");
+ "\" title=\""); out.write(b64);
out.write("\" title=\"");
out.write(_("Delete the .torrent file and the associated data file(s)")); out.write(_("Delete the .torrent file and the associated data file(s)"));
out.write("\" onclick=\"if (!confirm('"); out.write("\" onclick=\"if (!confirm('");
// Can't figure out how to escape double quotes inside the onclick string. // Can't figure out how to escape double quotes inside the onclick string.
// Single quotes in translate strings with parameters must be doubled. // Single quotes in translate strings with parameters must be doubled.
// Then the remaining single quite must be escaped // Then the remaining single quite must be escaped
out.write(_("Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?", fullFilename)); out.write(_("Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?", fullFilename));
out.write("')) { return false; }\">"); out.write("')) { return false; }\"");
out.write("<img src=\"/themes/snark/ubergine/images/delete.png\" title=\""); out.write(" src=\"/themes/snark/ubergine/images/delete.png\" alt=\"");
out.write(_("Delete the .torrent file and the associated data file(s)"));
out.write("\" alt=\"");
out.write(_("Delete")); out.write(_("Delete"));
out.write("\">"); out.write("\">");
out.write("</a>");
} }
out.write("</td>\n</tr>\n"); out.write("</td>\n</tr>\n");

View File

@ -36,7 +36,7 @@ public class ConfigPeerHandler extends FormHandler {
return; return;
} }
addFormError(_("Invalid peer")); addFormError(_("Invalid peer"));
} else if (_action.equals(_("Adjust Profile Bonuses"))) { } else if (_action.equals(_("Adjust peer bonuses"))) {
Hash h = getHash(); Hash h = getHash();
if (h != null) { if (h != null) {
PeerProfile prof = _context.profileOrganizer().getProfile(h); PeerProfile prof = _context.profileOrganizer().getProfile(h);
@ -59,6 +59,8 @@ public class ConfigPeerHandler extends FormHandler {
addFormError(_("Invalid peer")); addFormError(_("Invalid peer"));
} else if (_action.startsWith("Check")) { } else if (_action.startsWith("Check")) {
addFormError(_("Unsupported")); addFormError(_("Unsupported"));
} else {
addFormError("Unknown action \"" + _action + '"');
} }
} }

View File

@ -20,26 +20,23 @@ public class FormHandler {
protected Log _log; protected Log _log;
private String _nonce; private String _nonce;
protected String _action; protected String _action;
protected String _method;
protected String _passphrase; protected String _passphrase;
private List<String> _errors; private final List<String> _errors;
private List<String> _notices; private final List<String> _notices;
private boolean _processed; private boolean _processed;
private boolean _valid; private boolean _valid;
public FormHandler() { public FormHandler() {
_errors = new ArrayList(); _errors = new ArrayList();
_notices = new ArrayList(); _notices = new ArrayList();
_action = null;
_processed = false;
_valid = true; _valid = true;
_nonce = null;
_passphrase = null;
} }
/** /**
* Configure this bean to query a particular router context * Configure this bean to query a particular router context
* *
* @param contextId begging few characters of the routerHash, or null to pick * @param contextId beginning few characters of the routerHash, or null to pick
* the first one we come across. * the first one we come across.
*/ */
public void setContextId(String contextId) { public void setContextId(String contextId) {
@ -54,6 +51,14 @@ public class FormHandler {
public void setNonce(String val) { _nonce = val; } public void setNonce(String val) { _nonce = val; }
public void setAction(String val) { _action = val; } public void setAction(String val) { _action = val; }
public void setPassphrase(String val) { _passphrase = val; } public void setPassphrase(String val) { _passphrase = val; }
/**
* Call this to prevent changes using GET
*
* @param the request method
* @since 0.8.2
*/
public void storeMethod(String val) { _method = val; }
/** /**
* Override this to perform the final processing (in turn, adding formNotice * Override this to perform the final processing (in turn, adding formNotice
@ -145,6 +150,12 @@ public class FormHandler {
_valid = false; _valid = false;
return; return;
} }
// To prevent actions with GET, jsps must call storeMethod()
if (_method != null && !"POST".equals(_method)) {
addFormError("Invalid form submission, requires POST not " + _method);
_valid = false;
return;
}
String sharedNonce = System.getProperty("router.consoleNonce"); String sharedNonce = System.getProperty("router.consoleNonce");
if ( (sharedNonce != null) && (sharedNonce.equals(_nonce) ) ) { if ( (sharedNonce != null) && (sharedNonce.equals(_nonce) ) ) {
@ -211,4 +222,8 @@ public class FormHandler {
return Messages.getString(s, o, _context); return Messages.getString(s, o, _context);
} }
/** two params @since 0.8.2 */
public String _(String s, Object o, Object o2) {
return Messages.getString(s, o, o2, _context);
}
} }

View File

@ -1,6 +1,7 @@
package net.i2p.router.web; package net.i2p.router.web;
import java.io.IOException; import java.io.IOException;
import java.io.Writer;
import java.util.Comparator; import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -9,7 +10,8 @@ import java.util.TreeSet;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.stat.Rate; import net.i2p.stat.Rate;
public class GraphHelper extends HelperBase { public class GraphHelper extends FormHandler {
protected Writer _out;
private int _periodCount; private int _periodCount;
private boolean _showEvents; private boolean _showEvents;
private int _width; private int _width;
@ -29,9 +31,6 @@ public class GraphHelper extends HelperBase {
static final int MAX_Y = 1024; static final int MAX_Y = 1024;
private static final int MIN_REFRESH = 15; private static final int MIN_REFRESH = 15;
public GraphHelper() {
}
/** set the defaults after we have a context */ /** set the defaults after we have a context */
@Override @Override
public void setContextId(String contextId) { public void setContextId(String contextId) {
@ -43,6 +42,12 @@ public class GraphHelper extends HelperBase {
_showEvents = Boolean.valueOf(_context.getProperty(PROP_EVENTS)).booleanValue(); _showEvents = Boolean.valueOf(_context.getProperty(PROP_EVENTS)).booleanValue();
} }
/**
* This was a HelperBase but now it's a FormHandler
* @since 0.8.2
*/
public void storeWriter(Writer out) { _out = out; }
public void setPeriodCount(String str) { public void setPeriodCount(String str) {
try { _periodCount = Integer.parseInt(str); } catch (NumberFormatException nfe) {} try { _periodCount = Integer.parseInt(str); } catch (NumberFormatException nfe) {}
} }
@ -125,10 +130,15 @@ public class GraphHelper extends HelperBase {
} }
public String getForm() { public String getForm() {
saveSettings(); String prev = System.getProperty("net.i2p.router.web.GraphHelper.nonce");
if (prev != null) System.setProperty("net.i2p.router.web.GraphHelper.noncePrev", prev);
String nonce = "" + _context.random().nextLong();
System.setProperty("net.i2p.router.web.GraphHelper.nonce", nonce);
try { try {
_out.write("<br><h3>" + _("Configure Graph Display") + " [<a href=\"configstats\">" + _("Select Stats") + "</a>]</h3>"); _out.write("<br><h3>" + _("Configure Graph Display") + " [<a href=\"configstats\">" + _("Select Stats") + "</a>]</h3>");
_out.write("<form action=\"graphs\" method=\"POST\">"); _out.write("<form action=\"graphs\" method=\"POST\">\n" +
"<input type=\"hidden\" name=\"action\" value=\"foo\">\n" +
"<input type=\"hidden\" name=\"nonce\" value=\"" + nonce + "\" >\n");
_out.write(_("Periods") + ": <input size=\"3\" type=\"text\" name=\"periodCount\" value=\"" + _periodCount + "\"><br>\n"); _out.write(_("Periods") + ": <input size=\"3\" type=\"text\" name=\"periodCount\" value=\"" + _periodCount + "\"><br>\n");
_out.write(_("Plot averages") + ": <input type=\"radio\" class=\"optbox\" name=\"showEvents\" value=\"false\" " + (_showEvents ? "" : "checked=\"true\" ") + "> "); _out.write(_("Plot averages") + ": <input type=\"radio\" class=\"optbox\" name=\"showEvents\" value=\"false\" " + (_showEvents ? "" : "checked=\"true\" ") + "> ");
_out.write(_("or")+ " " +_("plot events") + ": <input type=\"radio\" class=\"optbox\" name=\"showEvents\" value=\"true\" "+ (_showEvents ? "checked=\"true\" " : "") + "><br>\n"); _out.write(_("or")+ " " +_("plot events") + ": <input type=\"radio\" class=\"optbox\" name=\"showEvents\" value=\"true\" "+ (_showEvents ? "checked=\"true\" " : "") + "><br>\n");
@ -143,6 +153,15 @@ public class GraphHelper extends HelperBase {
return ""; return "";
} }
/**
* This was a HelperBase but now it's a FormHandler
* @since 0.8.2
*/
@Override
protected void processForm() {
saveSettings();
}
/** /**
* Silently save settings if changed, no indication of success or failure * Silently save settings if changed, no indication of success or failure
* @since 0.7.10 * @since 0.7.10
@ -159,6 +178,7 @@ public class GraphHelper extends HelperBase {
_context.router().setConfigSetting(PROP_REFRESH, "" + _refreshDelaySeconds); _context.router().setConfigSetting(PROP_REFRESH, "" + _refreshDelaySeconds);
_context.router().setConfigSetting(PROP_EVENTS, "" + _showEvents); _context.router().setConfigSetting(PROP_EVENTS, "" + _showEvents);
_context.router().saveConfig(); _context.router().saveConfig();
addFormNotice(_("Graph settings saved"));
} }
} }

View File

@ -28,7 +28,13 @@ public abstract class HelperBase {
/** might be useful in the jsp's */ /** might be useful in the jsp's */
//public RouterContext getContext() { return _context; } //public RouterContext getContext() { return _context; }
public void setWriter(Writer out) { _out = out; }
/**
* Renamed from setWriter, we realy don't want setFoo(non-String)
* Prevent jsp.error.beans.property.conversion 500 error for ?writer=foo
* @since 0.8.2
*/
public void storeWriter(Writer out) { _out = out; }
/** translate a string */ /** translate a string */
public String _(String s) { public String _(String s) {

View File

@ -16,6 +16,7 @@
<%@include file="confignav.jsi" %> <%@include file="confignav.jsi" %>
<jsp:useBean class="net.i2p.router.web.ConfigNetHandler" id="formhandler" scope="request" /> <jsp:useBean class="net.i2p.router.web.ConfigNetHandler" id="formhandler" scope="request" />
<% formhandler.storeMethod(request.getMethod()); %>
<jsp:setProperty name="formhandler" property="*" /> <jsp:setProperty name="formhandler" property="*" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:getProperty name="formhandler" property="allMessages" /> <jsp:getProperty name="formhandler" property="allMessages" />

View File

@ -18,6 +18,7 @@
<%@include file="confignav.jsi" %> <%@include file="confignav.jsi" %>
<jsp:useBean class="net.i2p.router.web.ConfigAdvancedHandler" id="formhandler" scope="request" /> <jsp:useBean class="net.i2p.router.web.ConfigAdvancedHandler" id="formhandler" scope="request" />
<% formhandler.storeMethod(request.getMethod()); %>
<jsp:setProperty name="formhandler" property="*" /> <jsp:setProperty name="formhandler" property="*" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:getProperty name="formhandler" property="allMessages" /> <jsp:getProperty name="formhandler" property="allMessages" />

View File

@ -21,6 +21,7 @@ button span.hide{
<%@include file="confignav.jsi" %> <%@include file="confignav.jsi" %>
<jsp:useBean class="net.i2p.router.web.ConfigClientsHandler" id="formhandler" scope="request" /> <jsp:useBean class="net.i2p.router.web.ConfigClientsHandler" id="formhandler" scope="request" />
<% formhandler.storeMethod(request.getMethod()); %>
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="formhandler" property="action" value="<%=request.getParameter("action")%>" /> <jsp:setProperty name="formhandler" property="action" value="<%=request.getParameter("action")%>" />
<jsp:setProperty name="formhandler" property="nonce" value="<%=request.getParameter("nonce")%>" /> <jsp:setProperty name="formhandler" property="nonce" value="<%=request.getParameter("nonce")%>" />

View File

@ -13,6 +13,7 @@
<%@include file="confignav.jsi" %> <%@include file="confignav.jsi" %>
<jsp:useBean class="net.i2p.router.web.ConfigKeyringHandler" id="formhandler" scope="request" /> <jsp:useBean class="net.i2p.router.web.ConfigKeyringHandler" id="formhandler" scope="request" />
<% formhandler.storeMethod(request.getMethod()); %>
<jsp:setProperty name="formhandler" property="*" /> <jsp:setProperty name="formhandler" property="*" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:getProperty name="formhandler" property="allMessages" /> <jsp:getProperty name="formhandler" property="allMessages" />

View File

@ -15,6 +15,7 @@
<%@include file="confignav.jsi" %> <%@include file="confignav.jsi" %>
<jsp:useBean class="net.i2p.router.web.ConfigLoggingHandler" id="formhandler" scope="request" /> <jsp:useBean class="net.i2p.router.web.ConfigLoggingHandler" id="formhandler" scope="request" />
<% formhandler.storeMethod(request.getMethod()); %>
<jsp:setProperty name="formhandler" property="*" /> <jsp:setProperty name="formhandler" property="*" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:getProperty name="formhandler" property="allMessages" /> <jsp:getProperty name="formhandler" property="allMessages" />

View File

@ -5,7 +5,7 @@
%> %>
<jsp:useBean class="net.i2p.router.web.ConfigNavHelper" id="navHelper" scope="request" /> <jsp:useBean class="net.i2p.router.web.ConfigNavHelper" id="navHelper" scope="request" />
<jsp:setProperty name="navHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="navHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="navHelper" property="writer" value="<%=out%>" /> <% navHelper.storeWriter(out); %>
<div class="confignav" id="confignav"> <div class="confignav" id="confignav">
<center> <center>
<% <%

View File

@ -13,6 +13,7 @@
<%@include file="confignav.jsi" %> <%@include file="confignav.jsi" %>
<jsp:useBean class="net.i2p.router.web.ConfigPeerHandler" id="formhandler" scope="request" /> <jsp:useBean class="net.i2p.router.web.ConfigPeerHandler" id="formhandler" scope="request" />
<% formhandler.storeMethod(request.getMethod()); %>
<jsp:setProperty name="formhandler" property="*" /> <jsp:setProperty name="formhandler" property="*" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:getProperty name="formhandler" property="allMessages" /> <jsp:getProperty name="formhandler" property="allMessages" />
@ -27,7 +28,7 @@
peer = net.i2p.data.DataHelper.stripHTML(request.getParameter("peer")); // XSS peer = net.i2p.data.DataHelper.stripHTML(request.getParameter("peer")); // XSS
%> %>
<div class="configure"> <div class="configure">
<form action="" method="POST"> <form action="configpeer" method="POST">
<% String prev = System.getProperty("net.i2p.router.web.ConfigPeerHandler.nonce"); <% String prev = System.getProperty("net.i2p.router.web.ConfigPeerHandler.nonce");
if (prev != null) System.setProperty("net.i2p.router.web.ConfigPeerHandler.noncePrev", prev); if (prev != null) System.setProperty("net.i2p.router.web.ConfigPeerHandler.noncePrev", prev);
System.setProperty("net.i2p.router.web.ConfigPeerHandler.nonce", new java.util.Random().nextLong()+""); %> System.setProperty("net.i2p.router.web.ConfigPeerHandler.nonce", new java.util.Random().nextLong()+""); %>
@ -64,7 +65,7 @@
<a name="shitlist"> </a><h2><%=intl._("Banned Peers")%></h2> <a name="shitlist"> </a><h2><%=intl._("Banned Peers")%></h2>
<jsp:useBean class="net.i2p.router.web.ProfilesHelper" id="profilesHelper" scope="request" /> <jsp:useBean class="net.i2p.router.web.ProfilesHelper" id="profilesHelper" scope="request" />
<jsp:setProperty name="profilesHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="profilesHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="profilesHelper" property="writer" value="<%=out%>" /> <% profilesHelper.storeWriter(out); %>
<jsp:getProperty name="profilesHelper" property="shitlistSummary" /> <jsp:getProperty name="profilesHelper" property="shitlistSummary" />
<div class="wideload"><h2><%=intl._("Banned IPs")%></h2> <div class="wideload"><h2><%=intl._("Banned IPs")%></h2>
<jsp:getProperty name="peerhelper" property="blocklistSummary" /> <jsp:getProperty name="peerhelper" property="blocklistSummary" />

View File

@ -13,6 +13,7 @@
<%@include file="confignav.jsi" %> <%@include file="confignav.jsi" %>
<jsp:useBean class="net.i2p.router.web.ConfigServiceHandler" id="formhandler" scope="request" /> <jsp:useBean class="net.i2p.router.web.ConfigServiceHandler" id="formhandler" scope="request" />
<% formhandler.storeMethod(request.getMethod()); %>
<jsp:setProperty name="formhandler" property="*" /> <jsp:setProperty name="formhandler" property="*" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:getProperty name="formhandler" property="allMessages" /> <jsp:getProperty name="formhandler" property="allMessages" />

View File

@ -58,8 +58,9 @@ function toggleAll(category)
<%@include file="confignav.jsi" %> <%@include file="confignav.jsi" %>
<jsp:useBean class="net.i2p.router.web.ConfigStatsHandler" id="formhandler" scope="request" /> <jsp:useBean class="net.i2p.router.web.ConfigStatsHandler" id="formhandler" scope="request" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <% formhandler.storeMethod(request.getMethod()); %>
<jsp:setProperty name="formhandler" property="*" /> <jsp:setProperty name="formhandler" property="*" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:getProperty name="formhandler" property="allMessages" /> <jsp:getProperty name="formhandler" property="allMessages" />
<jsp:useBean class="net.i2p.router.web.ConfigStatsHelper" id="statshelper" scope="request" /> <jsp:useBean class="net.i2p.router.web.ConfigStatsHelper" id="statshelper" scope="request" />

View File

@ -15,6 +15,7 @@
<div class="main" id="main"> <div class="main" id="main">
<%@include file="confignav.jsi" %> <%@include file="confignav.jsi" %>
<jsp:useBean class="net.i2p.router.web.ConfigTunnelsHandler" id="formhandler" scope="request" /> <jsp:useBean class="net.i2p.router.web.ConfigTunnelsHandler" id="formhandler" scope="request" />
<% formhandler.storeMethod(request.getMethod()); %>
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="formhandler" property="shouldsave" value="<%=request.getParameter("shouldsave")%>" /> <jsp:setProperty name="formhandler" property="shouldsave" value="<%=request.getParameter("shouldsave")%>" />
<jsp:setProperty name="formhandler" property="action" value="<%=request.getParameter("action")%>" /> <jsp:setProperty name="formhandler" property="action" value="<%=request.getParameter("action")%>" />

View File

@ -18,6 +18,7 @@
<%@include file="confignav.jsi" %> <%@include file="confignav.jsi" %>
<jsp:useBean class="net.i2p.router.web.ConfigUIHandler" id="formhandler" scope="request" /> <jsp:useBean class="net.i2p.router.web.ConfigUIHandler" id="formhandler" scope="request" />
<% formhandler.storeMethod(request.getMethod()); %>
<jsp:setProperty name="formhandler" property="*" /> <jsp:setProperty name="formhandler" property="*" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:getProperty name="formhandler" property="allMessages" /> <jsp:getProperty name="formhandler" property="allMessages" />

View File

@ -13,6 +13,7 @@
<%@include file="confignav.jsi" %> <%@include file="confignav.jsi" %>
<jsp:useBean class="net.i2p.router.web.ConfigUpdateHandler" id="formhandler" scope="request" /> <jsp:useBean class="net.i2p.router.web.ConfigUpdateHandler" id="formhandler" scope="request" />
<% formhandler.storeMethod(request.getMethod()); %>
<jsp:setProperty name="formhandler" property="*" /> <jsp:setProperty name="formhandler" property="*" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:getProperty name="formhandler" property="allMessages" /> <jsp:getProperty name="formhandler" property="allMessages" />

View File

@ -1,5 +1,5 @@
<%@page contentType="text/plain" <%@page contentType="text/plain"
%><jsp:useBean id="helper" class="net.i2p.router.web.StatHelper" %><jsp:useBean id="helper" class="net.i2p.router.web.StatHelper"
/><jsp:setProperty name="helper" property="peer" value="<%=request.getParameter("peer")%>" /><jsp:setProperty name="helper" property="peer" value="<%=request.getParameter("peer")%>"
/><jsp:setProperty name="helper" property="writer" value="<%=out%>" /><% helper.storeWriter(out);
/><jsp:getProperty name="helper" property="profile" /> %><jsp:getProperty name="helper" property="profile" />

View File

@ -13,9 +13,12 @@
<div class="graphspanel"> <div class="graphspanel">
<div class="widepanel"> <div class="widepanel">
<jsp:useBean class="net.i2p.router.web.GraphHelper" id="graphHelper" scope="request" /> <jsp:useBean class="net.i2p.router.web.GraphHelper" id="graphHelper" scope="request" />
<% graphHelper.storeMethod(request.getMethod()); %>
<jsp:setProperty name="graphHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="graphHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<% /* GraphHelper sets the defaults in setContextId, so setting the properties must be after the context */ %>
<jsp:setProperty name="graphHelper" property="*" /> <jsp:setProperty name="graphHelper" property="*" />
<jsp:setProperty name="graphHelper" property="writer" value="<%=out%>" /> <% graphHelper.storeWriter(out); %>
<jsp:getProperty name="graphHelper" property="allMessages" />
<jsp:getProperty name="graphHelper" property="images" /> <jsp:getProperty name="graphHelper" property="images" />
<jsp:getProperty name="graphHelper" property="form" /> <jsp:getProperty name="graphHelper" property="form" />
</div></div></div></body></html> </div></div></div></body></html>

View File

@ -3,6 +3,6 @@
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
</head> </head>
<body> <body>
The I2PSnark Anonymous BitTorrent Client is not running. Please visit the <a href="/configclients.jsp">config clients page</a> The I2PSnark Anonymous BitTorrent Client is not running. Please visit the <a href="/configclients#webapp">config clients page</a>
to start it. to start it.
</body></html> </body></html>

View File

@ -3,5 +3,5 @@
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
</head> </head>
<body> <body>
The I2P Tunnel Manager is not currently running. Please visit the <a href="/configclients.jsp">Client Configuration</a> page to start it. The I2P Tunnel Manager is not currently running. Please visit the <a href="/configclients#webapp">Client Configuration</a> page to start it.
</body></html> </body></html>

View File

@ -10,6 +10,6 @@
<div class="main" id="main"> <div class="main" id="main">
<jsp:useBean class="net.i2p.router.web.JobQueueHelper" id="jobQueueHelper" scope="request" /> <jsp:useBean class="net.i2p.router.web.JobQueueHelper" id="jobQueueHelper" scope="request" />
<jsp:setProperty name="jobQueueHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="jobQueueHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="jobQueueHelper" property="writer" value="<%=out%>" /> <% jobQueueHelper.storeWriter(out); %>
<jsp:getProperty name="jobQueueHelper" property="jobQueueSummary" /> <jsp:getProperty name="jobQueueHelper" property="jobQueueSummary" />
<hr></div></body></html> <hr></div></body></html>

View File

@ -10,7 +10,8 @@
<h1><%=intl._("I2P Router Logs")%></h1> <h1><%=intl._("I2P Router Logs")%></h1>
<div class="main" id="main"> <div class="main" id="main">
<div class="joblog"><h3><%=intl._("I2P Version & Running Environment")%></h3><a name="version"> </a> <div class="joblog"><h3><%=intl._("I2P Version & Running Environment")%></h3><a name="version"> </a>
<p><%=intl._("Please report bugs on <a href=\"http://trac.i2p2.i2p/newticket\">trac.i2p2.i2p</a>.")%> <p><%=intl._("Please report bugs on <a href=\"http://trac.i2p2.i2p/newticket\">trac.i2p2.i2p</a> or <a href=\"http://trac.i2p2.de/newticket\">trac.i2p2.de</a>.")%>
<%=intl._("You may use the username \"guest\" and password \"guest\" if you do not wish to register.")%>
<p><i><%=intl._("Please include this information in bug reports")%>:</i> <p><i><%=intl._("Please include this information in bug reports")%>:</i>
<p> <p>
<b>I2P version:</b> <jsp:getProperty name="helper" property="version" /><br> <b>I2P version:</b> <jsp:getProperty name="helper" property="version" /><br>

View File

@ -12,7 +12,7 @@
<div class="wideload"> <div class="wideload">
<jsp:useBean class="net.i2p.router.web.NetDbHelper" id="netdbHelper" scope="request" /> <jsp:useBean class="net.i2p.router.web.NetDbHelper" id="netdbHelper" scope="request" />
<jsp:setProperty name="netdbHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="netdbHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="netdbHelper" property="writer" value="<%=out%>" /> <% netdbHelper.storeWriter(out); %>
<jsp:setProperty name="netdbHelper" property="full" value="<%=request.getParameter("f")%>" /> <jsp:setProperty name="netdbHelper" property="full" value="<%=request.getParameter("f")%>" />
<jsp:setProperty name="netdbHelper" property="router" value="<%=request.getParameter("r")%>" /> <jsp:setProperty name="netdbHelper" property="router" value="<%=request.getParameter("r")%>" />
<jsp:setProperty name="netdbHelper" property="lease" value="<%=request.getParameter("l")%>" /> <jsp:setProperty name="netdbHelper" property="lease" value="<%=request.getParameter("l")%>" />

View File

@ -12,7 +12,7 @@
<%@include file="summary.jsi" %> <%@include file="summary.jsi" %>
<jsp:useBean class="net.i2p.router.web.OldConsoleHelper" id="conhelper" scope="request" /> <jsp:useBean class="net.i2p.router.web.OldConsoleHelper" id="conhelper" scope="request" />
<jsp:setProperty name="conhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="conhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="conhelper" property="writer" value="<%=out%>" /> <% conhelper.storeWriter(out); %>
<h1>I2P Router &raquo; Old Console</h1> <h1>I2P Router &raquo; Old Console</h1>
<div class="main" id="main"> <div class="main" id="main">
<jsp:getProperty name="conhelper" property="console" /> <jsp:getProperty name="conhelper" property="console" />

View File

@ -11,7 +11,7 @@
<div class="main" id="main"><div class="wideload"> <div class="main" id="main"><div class="wideload">
<jsp:useBean class="net.i2p.router.web.PeerHelper" id="peerHelper" scope="request" /> <jsp:useBean class="net.i2p.router.web.PeerHelper" id="peerHelper" scope="request" />
<jsp:setProperty name="peerHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="peerHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="peerHelper" property="writer" value="<%=out%>" /> <% peerHelper.storeWriter(out); %>
<jsp:setProperty name="peerHelper" property="urlBase" value="peers.jsp" /> <jsp:setProperty name="peerHelper" property="urlBase" value="peers.jsp" />
<jsp:setProperty name="peerHelper" property="sort" value="<%=request.getParameter("sort") != null ? request.getParameter("sort") : ""%>" /> <jsp:setProperty name="peerHelper" property="sort" value="<%=request.getParameter("sort") != null ? request.getParameter("sort") : ""%>" />
<jsp:getProperty name="peerHelper" property="peerSummary" /> <jsp:getProperty name="peerHelper" property="peerSummary" />

View File

@ -10,7 +10,7 @@
<div class="main" id="main"><div class="wideload"> <div class="main" id="main"><div class="wideload">
<jsp:useBean class="net.i2p.router.web.ProfilesHelper" id="profilesHelper" scope="request" /> <jsp:useBean class="net.i2p.router.web.ProfilesHelper" id="profilesHelper" scope="request" />
<jsp:setProperty name="profilesHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="profilesHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="profilesHelper" property="writer" value="<%=out%>" /> <% profilesHelper.storeWriter(out); %>
<jsp:setProperty name="profilesHelper" property="full" value="<%=request.getParameter("f")%>" /> <jsp:setProperty name="profilesHelper" property="full" value="<%=request.getParameter("f")%>" />
<jsp:getProperty name="profilesHelper" property="profileSummary" /> <jsp:getProperty name="profilesHelper" property="profileSummary" />
<a name="shitlist"> </a><h2><%=intl._("Banned Peers")%></h2> <a name="shitlist"> </a><h2><%=intl._("Banned Peers")%></h2>

View File

@ -9,7 +9,7 @@
<%@include file="summary.jsi" %> <%@include file="summary.jsi" %>
<jsp:useBean class="net.i2p.router.web.OldConsoleHelper" id="oldhelper" scope="request" /> <jsp:useBean class="net.i2p.router.web.OldConsoleHelper" id="oldhelper" scope="request" />
<jsp:setProperty name="oldhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="oldhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="oldhelper" property="writer" value="<%=out%>" /> <% oldhelper.storeWriter(out); %>
<jsp:setProperty name="oldhelper" property="full" value="<%=request.getParameter("f")%>" /> <jsp:setProperty name="oldhelper" property="full" value="<%=request.getParameter("f")%>" />
<h1><%=intl._("I2P Router Statistics")%></h1> <h1><%=intl._("I2P Router Statistics")%></h1>
<div class="main" id="main"> <div class="main" id="main">

View File

@ -11,7 +11,7 @@
<jsp:setProperty name="helper" property="updateNonce" value="<%=request.getParameter("updateNonce")%>" /> <jsp:setProperty name="helper" property="updateNonce" value="<%=request.getParameter("updateNonce")%>" />
<jsp:setProperty name="helper" property="consoleNonce" value="<%=request.getParameter("consoleNonce")%>" /> <jsp:setProperty name="helper" property="consoleNonce" value="<%=request.getParameter("consoleNonce")%>" />
<jsp:setProperty name="helper" property="requestURI" value="<%=request.getRequestURI()%>" /> <jsp:setProperty name="helper" property="requestURI" value="<%=request.getRequestURI()%>" />
<jsp:setProperty name="helper" property="writer" value="<%=out%>" /> <% helper.storeWriter(out); %>
<% <%
/* /*
* The following is required for the reseed button to work, although we probably * The following is required for the reseed button to work, although we probably

View File

@ -3,6 +3,6 @@
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
</head> </head>
<body> <body>
SusiDNS is not running. Go to <a href="/configclients.jsp">the config clients page</a> SusiDNS is not running. Go to <a href="/configclients#webapp">the config clients page</a>
to start it. to start it.
</body></html> </body></html>

View File

@ -3,6 +3,6 @@
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
</head> </head>
<body> <body>
SusiMail is not running. Go to <a href="/configclients.jsp">the config clients page</a> SusiMail is not running. Go to <a href="/configclients#webapp">the config clients page</a>
to start it. to start it.
</body></html> </body></html>

View File

@ -10,6 +10,6 @@
<div class="main" id="main"> <div class="main" id="main">
<jsp:useBean class="net.i2p.router.web.TunnelHelper" id="tunnelHelper" scope="request" /> <jsp:useBean class="net.i2p.router.web.TunnelHelper" id="tunnelHelper" scope="request" />
<jsp:setProperty name="tunnelHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="tunnelHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="tunnelHelper" property="writer" value="<%=out%>" /> <% tunnelHelper.storeWriter(out); %>
<jsp:getProperty name="tunnelHelper" property="tunnelSummary" /> <jsp:getProperty name="tunnelHelper" property="tunnelSummary" />
</div></body></html> </div></body></html>

View File

@ -98,6 +98,7 @@ public class Connection {
_log = _context.logManager().getLog(Connection.class); _log = _context.logManager().getLog(Connection.class);
_receiver = new ConnectionDataReceiver(_context, this); _receiver = new ConnectionDataReceiver(_context, this);
_inputStream = new MessageInputStream(_context); _inputStream = new MessageInputStream(_context);
// FIXME pass through a passive flush delay setting as the 4th arg
_outputStream = new MessageOutputStream(_context, _receiver, (opts == null ? Packet.MAX_PAYLOAD_SIZE : opts.getMaxMessageSize())); _outputStream = new MessageOutputStream(_context, _receiver, (opts == null ? Packet.MAX_PAYLOAD_SIZE : opts.getMaxMessageSize()));
_outboundPackets = new TreeMap(); _outboundPackets = new TreeMap();
_options = (opts != null ? opts : new ConnectionOptions()); _options = (opts != null ? opts : new ConnectionOptions());

View File

@ -245,7 +245,7 @@
<delete file="debian/substvars"/> <delete file="debian/substvars"/>
</target> </target>
<target name="distclean" depends="clean"> <target name="distclean" depends="clean">
<delete includeemptydirs="true" removeNotFollowedSymlinks="true" failonerror="false" > <delete includeemptydirs="true" failonerror="false" >
<fileset dir="debian/packages" followSymlinks="false" /> <fileset dir="debian/packages" followSymlinks="false" />
</delete> </delete>
<delete dir="debian/repo" /> <delete dir="debian/repo" />
@ -368,7 +368,7 @@
</copy> </copy>
</target> </target>
<target name="preppkg-base" depends="build, preplicenses, prepconsoleDocs"> <target name="preppkg-base" depends="build, preplicenses, prepConsoleDocs, prepthemeupdates, prepCertificates">
<!-- if updater200 was run previously, it left *.pack files in pkg-temp --> <!-- if updater200 was run previously, it left *.pack files in pkg-temp -->
<delete> <delete>
<fileset dir="pkg-temp" includes="**/*.jar.pack **/*.war.pack" /> <fileset dir="pkg-temp" includes="**/*.jar.pack **/*.war.pack" />
@ -419,10 +419,6 @@
<copy file="installer/resources/start.ico" todir="pkg-temp/docs/" /> <copy file="installer/resources/start.ico" todir="pkg-temp/docs/" />
<copy file="installer/resources/console.ico" todir="pkg-temp/docs/" /> <copy file="installer/resources/console.ico" todir="pkg-temp/docs/" />
<copy file="installer/resources/uninstall.ico" todir="pkg-temp/docs/" /> <copy file="installer/resources/uninstall.ico" todir="pkg-temp/docs/" />
<mkdir dir="pkg-temp/docs/themes/" />
<copy todir="pkg-temp/docs/themes/" >
<fileset dir="installer/resources/themes/" />
</copy>
<!-- Eepsite stuff here --> <!-- Eepsite stuff here -->
<mkdir dir="pkg-temp/eepsite" /> <mkdir dir="pkg-temp/eepsite" />
<mkdir dir="pkg-temp/eepsite/webapps" /> <mkdir dir="pkg-temp/eepsite/webapps" />
@ -455,42 +451,17 @@
<copy file="installer/lib/launch4j/lib/JGoodies.Looks.LICENSE.txt" tofile="pkg-temp/licenses/LICENSE-JGoodies-Looks.txt" /> <copy file="installer/lib/launch4j/lib/JGoodies.Looks.LICENSE.txt" tofile="pkg-temp/licenses/LICENSE-JGoodies-Looks.txt" />
<copy file="installer/lib/launch4j/lib/XStream.LICENSE.txt" tofile="pkg-temp/licenses/LICENSE-XStream.txt" /> <copy file="installer/lib/launch4j/lib/XStream.LICENSE.txt" tofile="pkg-temp/licenses/LICENSE-XStream.txt" />
</target> </target>
<target name="prepthemeupdates"> <target name="prepthemeupdates">
<!-- Migrated all Snark content to its own dir. Need to ensure snark dir excluded from console theme choices!! --> <copy todir="pkg-temp/docs/themes/" >
<!-- Snark's visible Assets --> <fileset dir="installer/resources/themes/" />
<copy todir="pkg-temp/docs/themes/snark/ubergine/" >
<fileset dir="installer/resources/themes/snark/ubergine/" />
</copy> </copy>
<!-- No need to copy these individually, we're copying the whole dir below.. </target>
<copy file="installer/resources/themes/console/images/favicon.ico" todir="pkg-temp/docs/themes/console/images/" />
<copy file="installer/resources/themes/console/images/i2plogo.png" todir="pkg-temp/docs/themes/console/images/" /> <!-- SSL Certs -->
--> <target name="prepCertificates">
<!-- Since the logo moved, we have to update the error pages --> <copy todir="pkg-temp/certificates/" >
<copy todir="pkg-temp/docs/" > <fileset dir="installer/resources/certificates/" />
<fileset dir="installer/resources/proxy" />
</copy>
<!-- make a "classic" theme -->
<copy todir="pkg-temp/docs/themes/console/classic/" >
<fileset dir="installer/resources/themes/console/classic/" />
</copy>
<!-- Add dark theme -->
<copy todir="pkg-temp/docs/themes/console/dark/" >
<fileset dir="installer/resources/themes/console/dark/" />
</copy>
<!-- Add light theme -->
<copy todir="pkg-temp/docs/themes/console/light/" >
<fileset dir="installer/resources/themes/console/light/" />
</copy>
<!-- Add midnight theme -->
<copy todir="pkg-temp/docs/themes/console/midnight/" >
<fileset dir="installer/resources/themes/console/midnight/" />
</copy>
<!-- Add shared images.. these are subject to flux and change! -->
<copy todir="pkg-temp/docs/themes/console/images/" >
<fileset dir="installer/resources/themes/console/images/" />
</copy>
<copy todir="pkg-temp/docs/" >
<fileset dir="installer/resources/readme/" includes="readme*.html" />
</copy> </copy>
</target> </target>
@ -500,16 +471,23 @@
<tarfileset dir="pkg-temp" includes="**/*" prefix="i2p" /> <tarfileset dir="pkg-temp" includes="**/*" prefix="i2p" />
</tar> </tar>
</target> </target>
<target name="deletepkg-temp"> <target name="deletepkg-temp">
<delete dir="pkg-temp" /> <delete dir="pkg-temp" />
</target> </target>
<target name="prepconsoleDocs" depends="prepgeoupdate">
<!-- readme and proxy error page files, GeoIP files, and flag icons -->
<target name="prepConsoleDocs" depends="prepConsoleDocUpdates, prepgeoupdate" />
<!-- readme and proxy error page files -->
<target name="prepConsoleDocUpdates">
<copy todir="pkg-temp/docs/" > <copy todir="pkg-temp/docs/" >
<fileset dir="installer/resources/readme/" includes="readme*.html" /> <fileset dir="installer/resources/readme/" includes="readme*.html" />
<fileset dir="installer/resources/proxy" /> <fileset dir="installer/resources/proxy/" includes="*.ht" />
</copy> </copy>
</target> </target>
<target name="consoleDocs" depends="deletepkg-temp, prepconsoleDocs">
<target name="consoleDocs" depends="deletepkg-temp, prepConsoleDocs">
<zip destfile="docs.zip" basedir="pkg-temp" whenempty="fail" /> <zip destfile="docs.zip" basedir="pkg-temp" whenempty="fail" />
</target> </target>
@ -560,7 +538,8 @@
<copy file="core/java/build/i2ptest.jar" todir="pkg-temp/lib" /> <copy file="core/java/build/i2ptest.jar" todir="pkg-temp/lib" />
<zip destfile="i2pupdate.zip" basedir="pkg-temp" /> <zip destfile="i2pupdate.zip" basedir="pkg-temp" />
</target> </target>
<target name="prepupdate" depends="build2, prepupdateSmall">
<target name="prepupdate" depends="build2, prepupdateSmall, prepConsoleDocUpdates, prepCertificates">
<copy file="build/BOB.jar" todir="pkg-temp/lib/" /> <copy file="build/BOB.jar" todir="pkg-temp/lib/" />
<copy file="build/sam.jar" todir="pkg-temp/lib/" /> <copy file="build/sam.jar" todir="pkg-temp/lib/" />
<copy file="build/i2psnark.jar" todir="pkg-temp/lib" /> <copy file="build/i2psnark.jar" todir="pkg-temp/lib" />
@ -587,6 +566,7 @@
<copy file="installer/resources/news.xml" todir="pkg-temp/docs/" /> <copy file="installer/resources/news.xml" todir="pkg-temp/docs/" />
--> -->
</target> </target>
<target name="prepupdateSmall" depends="buildSmall, prepupdateRouter, prepthemeupdates"> <target name="prepupdateSmall" depends="buildSmall, prepupdateRouter, prepthemeupdates">
<copy file="build/i2ptunnel.jar" todir="pkg-temp/lib/" /> <copy file="build/i2ptunnel.jar" todir="pkg-temp/lib/" />
<copy file="build/mstreaming.jar" todir="pkg-temp/lib/" /> <copy file="build/mstreaming.jar" todir="pkg-temp/lib/" />
@ -601,10 +581,13 @@
<!-- decapitalized the file in 0.7.8 --> <!-- decapitalized the file in 0.7.8 -->
<copy file="installer/resources/countries.txt" todir="pkg-temp/geoip/" /> <copy file="installer/resources/countries.txt" todir="pkg-temp/geoip/" />
</target> </target>
<target name="prepupdateRouter" depends="buildrouter, deletepkg-temp"> <target name="prepupdateRouter" depends="buildrouter, deletepkg-temp">
<copy file="build/i2p.jar" todir="pkg-temp/lib/" /> <copy file="build/i2p.jar" todir="pkg-temp/lib/" />
<copy file="build/router.jar" todir="pkg-temp/lib/" /> <copy file="build/router.jar" todir="pkg-temp/lib/" />
</target> </target>
<!-- GeoIP files and flag icons -->
<target name="prepgeoupdate"> <target name="prepgeoupdate">
<copy file="installer/resources/geoip.txt" todir="pkg-temp/geoip/" /> <copy file="installer/resources/geoip.txt" todir="pkg-temp/geoip/" />
<copy file="installer/resources/countries.txt" todir="pkg-temp/geoip/" /> <copy file="installer/resources/countries.txt" todir="pkg-temp/geoip/" />
@ -612,6 +595,7 @@
<fileset dir="installer/resources/icons/flags" /> <fileset dir="installer/resources/icons/flags" />
</copy> </copy>
</target> </target>
<target name="prepjupdate" depends="prepupdate, buildWEB"> <target name="prepjupdate" depends="prepupdate, buildWEB">
<copy file="build/jasper-compiler.jar" todir="pkg-temp/lib/" /> <copy file="build/jasper-compiler.jar" todir="pkg-temp/lib/" />
<copy file="build/jasper-runtime.jar" todir="pkg-temp/lib/" /> <copy file="build/jasper-runtime.jar" todir="pkg-temp/lib/" />

View File

@ -456,7 +456,7 @@ public class EepGet {
for (int i = 0; i < _listeners.size(); i++) for (int i = 0; i < _listeners.size(); i++)
_listeners.get(i).attemptFailed(_url, _bytesTransferred, _bytesRemaining, _currentAttempt, _numRetries, ioe); _listeners.get(i).attemptFailed(_url, _bytesTransferred, _bytesRemaining, _currentAttempt, _numRetries, ioe);
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN))
_log.warn("ERR: doFetch failed " + ioe); _log.warn("ERR: doFetch failed ", ioe);
if (ioe instanceof MalformedURLException) if (ioe instanceof MalformedURLException)
_keepFetching = false; _keepFetching = false;
} finally { } finally {

View File

@ -1,55 +1,131 @@
package net.i2p.util; package net.i2p.util;
/*
* Contains code from:
* http://blogs.sun.com/andreas/resource/InstallCert.java
* http://blogs.sun.com/andreas/entry/no_more_unable_to_find
*
* ===============
*
* Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Sun Microsystems nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.security.KeyStore;
import java.security.GeneralSecurityException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
// all part of the CA experiment below import javax.net.ssl.TrustManagerFactory;
//import java.io.FileInputStream; import javax.net.ssl.X509TrustManager;
//import java.io.InputStream;
//import java.util.Enumeration;
//import java.security.KeyStore;
//import java.security.GeneralSecurityException;
//import java.security.cert.CertificateExpiredException;
//import java.security.cert.CertificateNotYetValidException;
//import java.security.cert.CertificateFactory;
//import java.security.cert.X509Certificate;
//import javax.net.ssl.KeyManagerFactory;
//import javax.net.ssl.SSLContext;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
/** /**
* HTTPS only, non-proxied only, no retries, no min and max size options, no timeout option * HTTPS only, non-proxied only, no retries, no min and max size options, no timeout option
* Fails on 301 or 302 (doesn't follow redirect) * Fails on 301 or 302 (doesn't follow redirect)
* Fails on self-signed certs (must have a valid cert chain) * Fails on bad certs (must have a valid cert chain)
* Self-signed certs or CAs not in the JVM key store must be loaded to be trusted.
*
* Since 0.8.2, loads additional trusted CA certs from $I2P/certificates/ and ~/.i2p/certificates/
* *
* @author zzz * @author zzz
* @since 0.7.10 * @since 0.7.10
*/ */
public class SSLEepGet extends EepGet { public class SSLEepGet extends EepGet {
//private static SSLContext _sslContext; /** if true, save cert chain on cert error */
private boolean _saveCerts;
/** true if called from main(), used for logging */
private boolean _commandLine;
/** may be null if init failed */
private final SSLContext _sslContext;
/** may be null if init failed */
private SavingTrustManager _stm;
/**
* A new SSLEepGet with a new SSLState
*/
public SSLEepGet(I2PAppContext ctx, OutputStream outputStream, String url) { public SSLEepGet(I2PAppContext ctx, OutputStream outputStream, String url) {
this(ctx, outputStream, url, null);
}
/**
* @param state an SSLState retrieved from a previous SSLEepGet with getSSLState(), or null.
* This makes repeated fetches from the same host MUCH faster,
* and prevents repeated key store loads even for different hosts.
* @since 0.8.2
*/
public SSLEepGet(I2PAppContext ctx, OutputStream outputStream, String url, SSLState state) {
// we're using this constructor: // we're using this constructor:
// public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, long minSize, long maxSize, String outputFile, OutputStream outputStream, String url, boolean allowCaching, String etag, String postData) { // public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, long minSize, long maxSize, String outputFile, OutputStream outputStream, String url, boolean allowCaching, String etag, String postData) {
super(ctx, false, null, -1, 0, -1, -1, null, outputStream, url, true, null, null); super(ctx, false, null, -1, 0, -1, -1, null, outputStream, url, true, null, null);
if (state != null && state.context != null)
_sslContext = state.context;
else
_sslContext = initSSLContext();
if (_sslContext == null)
_log.error("Failed to initialize custom SSL context, using default context");
} }
/** /**
* SSLEepGet url * SSLEepGet https://foo/bar
* no command line options supported * or to save cert chain:
* SSLEepGet -s https://foo/bar
*/ */
public static void main(String args[]) { public static void main(String args[]) {
String url = null; String url = null;
boolean saveCerts = false;
try { try {
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
if (args[i].startsWith("-")) { if (args[i].equals("-s")) {
saveCerts = true;
} else if (args[i].startsWith("-")) {
usage(); usage();
return; return;
} else { } else {
@ -77,94 +153,323 @@ public class SSLEepGet extends EepGet {
return; return;
} }
/****** SSLEepGet get = new SSLEepGet(I2PAppContext.getGlobalContext(), out, url);
* This is all an experiment to add a CA cert loaded from a file so we can use if (saveCerts)
* selfsigned certs on our servers. get._saveCerts = true;
* But it's failing. get._commandLine = true;
* Run as java -Djava.security.debug=certpath -Djavax.net.debug=trustmanager -cp $I2P/lib/i2p.jar net.i2p.util.SSLEepGet "$@"
* to see the problems. It isn't including the added cert in the Trust Anchor list.
******/
/******
String foo = System.getProperty("javax.net.ssl.keyStore");
if (foo == null) {
File cacerts = new File(System.getProperty("java.home"), "lib/security/cacerts");
foo = cacerts.getAbsolutePath();
}
System.err.println("Location is: " + foo);
try {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
try {
InputStream fis = new FileInputStream(foo);
ks.load(fis, "changeit".toCharArray());
fis.close();
} catch (GeneralSecurityException gse) {
System.err.println("KS error, no default keys: " + gse);
ks.load(null, "changeit".toCharArray());
} catch (IOException ioe) {
System.err.println("IO error, no default keys: " + ioe);
ks.load(null, "changeit".toCharArray());
}
addCert(ks, "cacert");
for(Enumeration<String> e = ks.aliases(); e.hasMoreElements();) {
String alias = e.nextElement();
System.err.println("Aliases: " + alias + " isCert? " + ks.isCertificateEntry(alias));
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, "".toCharArray());
SSLContext sslc = SSLContext.getInstance("SSL");
sslc.init(kmf.getKeyManagers(), null, null);
_sslContext = sslc;
} catch (GeneralSecurityException gse) {
System.err.println("KS error: " + gse);
return;
} catch (IOException ioe) {
System.err.println("IO error: " + ioe);
return;
}
*******/
EepGet get = new SSLEepGet(I2PAppContext.getGlobalContext(), out, url);
get.addStatusListener(get.new CLIStatusListener(1024, 40)); get.addStatusListener(get.new CLIStatusListener(1024, 40));
get.fetch(45*1000, -1, 60*1000); get.fetch(45*1000, -1, 60*1000);
} }
private static void usage() { private static void usage() {
System.err.println("SSLEepGet url"); System.err.println("Usage: SSLEepGet https://url");
System.err.println("To save unknown certs, use: SSLEepGet -s https://url");
} }
/****** /**
private static boolean addCert(KeyStore ks, String file) { * Loads certs from location of javax.net.ssl.keyStore property,
* else from $JAVA_HOME/lib/security/jssacacerts,
* else from $JAVA_HOME/lib/security/cacerts.
*
* Then adds certs found in the $I2P/certificates/ directory
* and in the ~/.i2p/certificates/ directory.
*
* @return null on failure
* @since 0.8.2
*/
private SSLContext initSSLContext() {
KeyStore ks;
try {
ks = KeyStore.getInstance(KeyStore.getDefaultType());
} catch (GeneralSecurityException gse) {
_log.error("Key Store init error", gse);
return null;
}
boolean success = false;
String override = System.getProperty("javax.net.ssl.keyStore");
if (override != null)
success = loadCerts(new File(override), ks);
if (!success)
success = loadCerts(new File(System.getProperty("java.home"), "lib/security/jssecacerts"), ks);
if (!success)
success = loadCerts(new File(System.getProperty("java.home"), "lib/security/cacerts"), ks);
if (!success) {
_log.error("All key store loads failed, will only load local certificates");
} else if (_log.shouldLog(Log.INFO)) {
int count = 0;
try { try {
InputStream fis = new FileInputStream(file); for(Enumeration<String> e = ks.aliases(); e.hasMoreElements();) {
CertificateFactory cf = CertificateFactory.getInstance("X.509"); String alias = e.nextElement();
X509Certificate cert = (X509Certificate)cf.generateCertificate(fis); if (ks.isCertificateEntry(alias))
fis.close(); count++;
System.err.println("Adding cert, Issuer: " + cert.getIssuerX500Principal());
try {
cert.checkValidity();
} catch (CertificateExpiredException cee) {
System.err.println("Warning - expired cert: " + cee);
} catch (CertificateNotYetValidException cnyve) {
System.err.println("Warning - not yet valid cert: " + cnyve);
} }
// use file name as alias } catch (Exception foo) {}
ks.setCertificateEntry(file, cert); _log.info("Loaded " + count + " default trusted certificates");
} catch (GeneralSecurityException gse) { }
System.err.println("Read cert error: " + gse);
return false; File dir = new File(_context.getBaseDir(), "certificates");
} catch (IOException ioe) { int adds = addCerts(dir, ks);
System.err.println("Read cert error: " + ioe); int totalAdds = adds;
return false; if (adds > 0 && _log.shouldLog(Log.INFO))
} _log.info("Loaded " + adds + " trusted certificates from " + dir.getAbsolutePath());
if (!_context.getBaseDir().getAbsolutePath().equals(_context.getConfigDir().getAbsolutePath())) {
dir = new File(_context.getConfigDir(), "certificates");
adds = addCerts(dir, ks);
totalAdds += adds;
if (adds > 0 && _log.shouldLog(Log.INFO))
_log.info("Loaded " + adds + " trusted certificates from " + dir.getAbsolutePath());
}
dir = new File(System.getProperty("user.dir"));
if (!_context.getBaseDir().getAbsolutePath().equals(dir.getAbsolutePath())) {
dir = new File(_context.getConfigDir(), "certificates");
adds = addCerts(dir, ks);
totalAdds += adds;
if (adds > 0 && _log.shouldLog(Log.INFO))
_log.info("Loaded " + adds + " trusted certificates from " + dir.getAbsolutePath());
}
if (_log.shouldLog(Log.INFO))
_log.info("Loaded total of " + totalAdds + " new trusted certificates");
try {
SSLContext sslc = SSLContext.getInstance("TLS");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
X509TrustManager defaultTrustManager = (X509TrustManager)tmf.getTrustManagers()[0];
_stm = new SavingTrustManager(defaultTrustManager);
sslc.init(null, new TrustManager[] {_stm}, null);
return sslc;
} catch (GeneralSecurityException gse) {
_log.error("Key Store update error", gse);
}
return null;
}
/**
* Load all X509 Certs from a key store File into a KeyStore
* Note that each call reinitializes the KeyStore
*
* @return success
* @since 0.8.2
*/
private boolean loadCerts(File file, KeyStore ks) {
if (!file.exists())
return false;
InputStream fis = null;
try {
fis = new FileInputStream(file);
// "changeit" is the default password
ks.load(fis, "changeit".toCharArray());
} catch (GeneralSecurityException gse) {
_log.error("KeyStore load error, no default keys: " + file.getAbsolutePath(), gse);
try {
// not clear if null is allowed for password
ks.load(null, "changeit".toCharArray());
} catch (Exception foo) {}
return false;
} catch (IOException ioe) {
_log.error("KeyStore load error, no default keys: " + file.getAbsolutePath(), ioe);
try {
ks.load(null, "changeit".toCharArray());
} catch (Exception foo) {}
return false;
} finally {
try { if (fis != null) fis.close(); } catch (IOException foo) {}
}
return true;
}
/**
* Load all X509 Certs from a directory and add them to the
* trusted set of certificates in the key store
*
* @return number successfully added
* @since 0.8.2
*/
private int addCerts(File dir, KeyStore ks) {
if (_log.shouldLog(Log.INFO))
_log.info("Looking for X509 Certificates in " + dir.getAbsolutePath());
int added = 0;
if (dir.exists() && dir.isDirectory()) {
File[] files = dir.listFiles();
if (files != null) {
for (int i = 0; i < files.length; i++) {
File f = files[i];
if (!f.isFile())
continue;
// use file name as alias
// https://www.sslshopper.com/ssl-converter.html
// No idea if all these formats can actually be read by CertificateFactory
String alias = f.getName().toLowerCase();
if (alias.endsWith(".crt") || alias.endsWith(".pem") || alias.endsWith(".key") ||
alias.endsWith(".der") || alias.endsWith(".key") || alias.endsWith(".p7b") ||
alias.endsWith(".p7c") || alias.endsWith(".pfx") || alias.endsWith(".p12"))
alias = alias.substring(0, alias.length() - 4);
boolean success = addCert(f, alias, ks);
if (success)
added++;
}
}
}
return added;
}
/**
* Load an X509 Cert from a file and add it to the
* trusted set of certificates in the key store
*
* @return success
* @since 0.8.2
*/
private boolean addCert(File file, String alias, KeyStore ks) {
InputStream fis = null;
try {
fis = new FileInputStream(file);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(fis);
if (_log.shouldLog(Log.INFO)) {
_log.info("Read X509 Certificate from " + file.getAbsolutePath() +
" Issuer: " + cert.getIssuerX500Principal() +
"; Valid From: " + cert.getNotBefore() +
" To: " + cert.getNotAfter());
}
try {
cert.checkValidity();
} catch (CertificateExpiredException cee) {
_log.error("Rejecting expired X509 Certificate: " + file.getAbsolutePath(), cee);
return false;
} catch (CertificateNotYetValidException cnyve) {
_log.error("Rejecting X509 Certificate not yet valid: " + file.getAbsolutePath(), cnyve);
return false;
}
ks.setCertificateEntry(alias, cert);
if (_log.shouldLog(Log.INFO))
_log.info("Now trusting X509 Certificate, Issuer: " + cert.getIssuerX500Principal());
} catch (GeneralSecurityException gse) {
_log.error("Error reading X509 Certificate: " + file.getAbsolutePath(), gse);
return false;
} catch (IOException ioe) {
_log.error("Error reading X509 Certificate: " + file.getAbsolutePath(), ioe);
return false;
} finally {
try { if (fis != null) fis.close(); } catch (IOException foo) {}
}
return true; return true;
} }
*******/
/**
* From http://blogs.sun.com/andreas/resource/InstallCert.java
* This just saves the certificate chain for later inspection.
* @since 0.8.2
*/
private static class SavingTrustManager implements X509TrustManager {
private final X509TrustManager tm;
private X509Certificate[] chain;
SavingTrustManager(X509TrustManager tm) {
this.tm = tm;
}
public X509Certificate[] getAcceptedIssuers() {
throw new UnsupportedOperationException();
}
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
throw new UnsupportedOperationException();
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
this.chain = chain;
tm.checkServerTrusted(chain, authType);
}
}
/**
* Modified from http://blogs.sun.com/andreas/resource/InstallCert.java
* @since 0.8.2
*/
private static void saveCerts(String host, SavingTrustManager stm) {
X509Certificate[] chain = stm.chain;
if (chain == null) {
System.out.println("Could not obtain server certificate chain");
return;
}
for (int k = 0; k < chain.length; k++) {
X509Certificate cert = chain[k];
String name = host + '-' + (k + 1) + ".crt";
System.out.println("NOTE: Saving untrusted X509 certificate as " + name);
System.out.println(" Issuer: " + cert.getIssuerX500Principal());
System.out.println(" Valid From: " + cert.getNotBefore());
System.out.println(" Valid To: " + cert.getNotAfter());
try {
cert.checkValidity();
} catch (Exception e) {
System.out.println(" WARNING: Certificate is not currently valid, it cannot be used");
}
saveCert(cert, new File(name));
}
System.out.println("NOTE: To trust them, copy the certificate file(s) to the certificates directory and rerun without the -s option");
System.out.println("NOTE: EepGet failed, certificate error follows:");
}
private static final int LINE_LENGTH = 64;
/**
* Modified from:
* http://www.exampledepot.com/egs/java.security.cert/ExportCert.html
*
* This method writes a certificate to a file in base64 format.
* @since 0.8.2
*/
private static void saveCert(Certificate cert, File file) {
OutputStream os = null;
try {
// Get the encoded form which is suitable for exporting
byte[] buf = cert.getEncoded();
os = new FileOutputStream(file);
PrintWriter wr = new PrintWriter(os);
wr.println("-----BEGIN CERTIFICATE-----");
String b64 = Base64.encode(buf, true); // true = use standard alphabet
for (int i = 0; i < b64.length(); i += LINE_LENGTH) {
wr.println(b64.substring(i, Math.min(i + LINE_LENGTH, b64.length())));
}
wr.println("-----END CERTIFICATE-----");
wr.flush();
} catch (CertificateEncodingException cee) {
System.out.println("Error writing X509 Certificate " + file.getAbsolutePath() + ' ' + cee);
} catch (IOException ioe) {
System.out.println("Error writing X509 Certificate " + file.getAbsolutePath() + ' ' + ioe);
} finally {
try { if (os != null) os.close(); } catch (IOException foo) {}
}
}
/**
* An opaque class for the caller to pass to repeated instantiations of SSLEepGet.
* @since 0.8.2
*/
public static class SSLState {
private SSLContext context;
private SSLState(SSLContext ctx) {
context = ctx;
}
}
/**
* Pass this back to the next SSLEepGet constructor for faster fetches.
* This may be called either after the constructor or after the fetch.
* @since 0.8.2
*/
public SSLState getSSLState() {
return new SSLState(_sslContext);
}
///// end of all the SSL stuff
///// start of overrides
@Override @Override
protected void doFetch(SocketTimeout timeout) throws IOException { protected void doFetch(SocketTimeout timeout) throws IOException {
_headersRead = false; _headersRead = false;
@ -288,15 +593,16 @@ public class SSLEepGet extends EepGet {
//try { //try {
URL url = new URL(_actualURL); URL url = new URL(_actualURL);
String host = null;
int port = 0;
if ("https".equals(url.getProtocol())) { if ("https".equals(url.getProtocol())) {
String host = url.getHost(); host = url.getHost();
int port = url.getPort(); port = url.getPort();
if (port == -1) if (port == -1)
port = 443; port = 443;
// part of the experiment above if (_sslContext != null)
//if (_sslContext != null) _proxy = _sslContext.getSocketFactory().createSocket(host, port);
// _proxy = _sslContext.getSocketFactory().createSocket(host, port); else
//else
_proxy = SSLSocketFactory.getDefault().createSocket(host, port); _proxy = SSLSocketFactory.getDefault().createSocket(host, port);
} else { } else {
throw new IOException("Only https supported: " + _actualURL); throw new IOException("Only https supported: " + _actualURL);
@ -309,8 +615,23 @@ public class SSLEepGet extends EepGet {
_proxyIn = _proxy.getInputStream(); _proxyIn = _proxy.getInputStream();
_proxyOut = _proxy.getOutputStream(); _proxyOut = _proxy.getOutputStream();
_proxyOut.write(DataHelper.getUTF8(req)); // This is where the cert errors happen
_proxyOut.flush(); try {
_proxyOut.write(DataHelper.getUTF8(req));
_proxyOut.flush();
} catch (SSLHandshakeException sslhe) {
// this maybe would be better done in the catch in super.fetch(), but
// then we'd have to copy it all over here.
_log.error("SSL negotiation error with " + host + ':' + port +
" - self-signed certificate or untrusted certificate authority?", sslhe);
if (_saveCerts && _stm != null)
saveCerts(host, _stm);
else if (_commandLine) {
System.out.println("FAILED (probably due to untrusted certificates) - Run with -s option to save certificates");
}
// this is an IOE
throw sslhe;
}
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Request flushed"); _log.debug("Request flushed");

View File

@ -1,3 +1,26 @@
2010-11-22 zzz
* Addressbook: Fix rename error on Windows (tkt 323 - thanks RN!)
* build.xml: Cleanup, fix distclean error in older ants.
* Console:
- Convert GraphHelper to a FormHandler
- Require POST for all forms
- Change the way we store the Writer to prevent problems
- Fix bonus setting on configpeer.jsp
- More ".jsp" removal
* i2psnark:
- Defer piece loading until required
- Stub out Extension message support
- Convert GET to POST, require POST
* NTCP: Log tweak
* SSLEepGet, Reseeder:
- Implement additional CA loading
- Provide facility to reuse SSL state for speed
- Provide facility to store previously untrusted certificates
- Add www.cacert.org cert to the installer and updater so
SSL on a.netdb.i2p2.de and c.netdb.i2p2.de will work
- Add SSL reseed hosts, prefer them by default
- Reseed message cleanup
2010-11-19 zzz 2010-11-19 zzz
* Addressbook * Addressbook
- Store last-fetched time so we don't always fetch subscriptions after restart - Store last-fetched time so we don't always fetch subscriptions after restart

View File

@ -0,0 +1,41 @@
-----BEGIN CERTIFICATE-----
MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO
BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi
MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ
ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ
8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6
zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y
fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7
w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc
G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k
epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q
laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ
QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU
fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826
YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w
ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY
gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe
MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0
IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy
dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw
czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0
dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl
aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC
AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg
b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB
ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc
nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg
18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c
gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl
Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY
sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T
SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF
CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum
GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk
zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW
omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD
-----END CERTIFICATE-----

View File

@ -474,6 +474,20 @@ input[type=submit]:active {
text-shadow: 0 !important; text-shadow: 0 !important;
} }
input[type=image] {
padding: 0 !important;
background: #000;
-moz-border-radius: 0px;
-khtml-border-radius: 0px;
border-radius: 0px;
border: medium none;
margin: 0 2px;
}
input[type=image]:hover {
border: 1px outset #bbb;
}
input[type=text]:active, input[type=text]:hover, input.r:hover { input[type=text]:active, input[type=text]:hover, input.r:hover {
background: #f60; background: #f60;
color: #fff; color: #fff;

View File

@ -0,0 +1,29 @@
Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Sun Microsystems nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

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

View File

@ -29,6 +29,8 @@ import net.i2p.util.Translate;
* specified below unless the I2P configuration property "i2p.reseedURL" is * specified below unless the I2P configuration property "i2p.reseedURL" is
* set. It always writes to ./netDb/, so don't mess with that. * set. It always writes to ./netDb/, so don't mess with that.
* *
* This is somewhat complicated by trying to log to three places - the console,
* the router log, and the wrapper log.
*/ */
public class Reseeder { public class Reseeder {
private static ReseedRunner _reseedRunner; private static ReseedRunner _reseedRunner;
@ -40,12 +42,25 @@ public class Reseeder {
private static final String DEFAULT_SEED_URL = private static final String DEFAULT_SEED_URL =
"http://a.netdb.i2p2.de/,http://b.netdb.i2p2.de/,http://c.netdb.i2p2.de/," + "http://a.netdb.i2p2.de/,http://b.netdb.i2p2.de/,http://c.netdb.i2p2.de/," +
"http://reseed.i2p-projekt.de/,http://i2pbote.net/netDb/,http://r31453.ovh.net/static_media/netDb/"; "http://reseed.i2p-projekt.de/,http://www.i2pbote.net/netDb/,http://r31453.ovh.net/static_media/netDb/";
/** @since 0.8.2 */
private static final String DEFAULT_SSL_SEED_URL =
"https://a.netdb.i2p2.de/,https://c.netdb.i2p2.de/," +
"https://www.i2pbote.net/netDb/";
private static final String PROP_INPROGRESS = "net.i2p.router.web.ReseedHandler.reseedInProgress"; private static final String PROP_INPROGRESS = "net.i2p.router.web.ReseedHandler.reseedInProgress";
/** the console shows this message while reseedInProgress == false */
private static final String PROP_ERROR = "net.i2p.router.web.ReseedHandler.errorMessage"; private static final String PROP_ERROR = "net.i2p.router.web.ReseedHandler.errorMessage";
/** the console shows this message while reseedInProgress == true */
private static final String PROP_STATUS = "net.i2p.router.web.ReseedHandler.statusMessage"; private static final String PROP_STATUS = "net.i2p.router.web.ReseedHandler.statusMessage";
public static final String PROP_PROXY_HOST = "router.reseedProxyHost"; public static final String PROP_PROXY_HOST = "router.reseedProxyHost";
public static final String PROP_PROXY_PORT = "router.reseedProxyPort"; public static final String PROP_PROXY_PORT = "router.reseedProxyPort";
/** @since 0.8.2 */
public static final String PROP_PROXY_ENABLE = "router.reseedProxyEnable";
/** @since 0.8.2 */
public static final String PROP_SSL_DISABLE = "router.reseedSSLDisable";
private static final String RESEED_TIPS = private static final String RESEED_TIPS =
_x("Ensure that nothing blocks outbound HTTP, check <a target=\"_top\" href=\"logs.jsp\">logs</a> " + _x("Ensure that nothing blocks outbound HTTP, check <a target=\"_top\" href=\"logs.jsp\">logs</a> " +
"and if nothing helps, read the <a target=\"_top\" href=\"http://www.i2p2.de/faq.html\">FAQ</a> about reseeding manually."); "and if nothing helps, read the <a target=\"_top\" href=\"http://www.i2p2.de/faq.html\">FAQ</a> about reseeding manually.");
@ -63,7 +78,6 @@ public class Reseeder {
if (_reseedRunner.isRunning()) { if (_reseedRunner.isRunning()) {
return; return;
} else { } else {
System.setProperty(PROP_INPROGRESS, "true");
// set to daemon so it doesn't hang a shutdown // set to daemon so it doesn't hang a shutdown
Thread reseed = new I2PAppThread(_reseedRunner, "Reseed", true); Thread reseed = new I2PAppThread(_reseedRunner, "Reseed", true);
reseed.start(); reseed.start();
@ -76,20 +90,46 @@ public class Reseeder {
private boolean _isRunning; private boolean _isRunning;
private String _proxyHost; private String _proxyHost;
private int _proxyPort; private int _proxyPort;
private SSLEepGet.SSLState _sslState;
public ReseedRunner() { public ReseedRunner() {
_isRunning = false; _isRunning = false;
System.clearProperty(PROP_ERROR);
System.setProperty(PROP_STATUS, _("Reseeding")); System.setProperty(PROP_STATUS, _("Reseeding"));
System.setProperty(PROP_INPROGRESS, "true");
} }
public boolean isRunning() { return _isRunning; } public boolean isRunning() { return _isRunning; }
/*
* Do it.
* We update PROP_ERROR here.
*/
public void run() { public void run() {
_isRunning = true; _isRunning = true;
_proxyHost = _context.getProperty(PROP_PROXY_HOST); _sslState = null; // start fresh
_proxyPort = _context.getProperty(PROP_PROXY_PORT, -1); if (_context.getBooleanProperty(PROP_PROXY_ENABLE)) {
_proxyHost = _context.getProperty(PROP_PROXY_HOST);
_proxyPort = _context.getProperty(PROP_PROXY_PORT, -1);
}
System.out.println("Reseed start"); System.out.println("Reseed start");
reseed(false); int total = reseed(false);
System.out.println("Reseed complete"); if (total >= 50) {
System.out.println("Reseed complete, " + total + " received");
System.clearProperty(PROP_ERROR);
} else if (total > 0) {
System.out.println("Reseed complete, only " + total + " received");
System.setProperty(PROP_ERROR, ngettext("Reseed fetched only 1 router.",
"Reseed fetched only {0} routers.", total));
} else {
System.out.println("Reseed failed, check network connection");
System.out.println(
"Ensure that nothing blocks outbound HTTP, check the logs, " +
"and if nothing helps, read the FAQ about reseeding manually.");
System.setProperty(PROP_ERROR, _("Reseed failed.") + ' ' + _(RESEED_TIPS));
}
System.setProperty(PROP_INPROGRESS, "false"); System.setProperty(PROP_INPROGRESS, "false");
System.clearProperty(PROP_STATUS);
_sslState = null; // don't hold ref
_isRunning = false; _isRunning = false;
} }
@ -112,16 +152,56 @@ public class Reseeder {
* the routerInfo-*.dat files from the specified URL (or the default) and * the routerInfo-*.dat files from the specified URL (or the default) and
* save them into this router's netDb dir. * save them into this router's netDb dir.
* *
* - If list specified in the properties, use it randomly, without regard to http/https
* - If SSL not disabled, use the https randomly then
* the http randomly
* - Otherwise just the http randomly.
*
* @param echoStatus apparently always false
* @return count of routerinfos successfully fetched
*/ */
private void reseed(boolean echoStatus) { private int reseed(boolean echoStatus) {
List URLList = new ArrayList(); List<String> URLList = new ArrayList();
String URLs = _context.getProperty("i2p.reseedURL", DEFAULT_SEED_URL); String URLs = _context.getProperty("i2p.reseedURL");
boolean defaulted = URLs == null;
boolean SSLDisable = _context.getBooleanProperty(PROP_SSL_DISABLE);
if (defaulted) {
if (SSLDisable)
URLs = DEFAULT_SEED_URL;
else
URLs = DEFAULT_SSL_SEED_URL;
}
StringTokenizer tok = new StringTokenizer(URLs, " ,"); StringTokenizer tok = new StringTokenizer(URLs, " ,");
while (tok.hasMoreTokens()) while (tok.hasMoreTokens())
URLList.add(tok.nextToken().trim()); URLList.add(tok.nextToken().trim());
Collections.shuffle(URLList); Collections.shuffle(URLList);
for (int i = 0; i < URLList.size() && _isRunning; i++) if (defaulted && !SSLDisable) {
reseedOne((String) URLList.get(i), echoStatus); // put the non-SSL at the end of the SSL
List<String> URLList2 = new ArrayList();
tok = new StringTokenizer(DEFAULT_SSL_SEED_URL, " ,");
while (tok.hasMoreTokens())
URLList2.add(tok.nextToken().trim());
Collections.shuffle(URLList2);
URLList.addAll(URLList2);
}
int total = 0;
for (int i = 0; i < URLList.size() && _isRunning; i++) {
String url = URLList.get(i);
int dl = reseedOne(url, echoStatus);
if (dl > 0) {
total += dl;
// remove alternate version if we haven't tried it yet
String alt;
if (url.startsWith("http://"))
alt = url.replace("http://", "https://");
else
alt = url.replace("https://", "http://");
int idx = URLList.indexOf(alt);
if (idx > i)
URLList.remove(i);
}
}
return total;
} }
/** /**
@ -138,22 +218,23 @@ public class Reseeder {
* *
* Jetty directory listings are not compatible, as they look like * Jetty directory listings are not compatible, as they look like
* HREF="/full/path/to/routerInfo-... * HREF="/full/path/to/routerInfo-...
*
* We update PROP_STATUS here.
*
* @param echoStatus apparently always false
* @return count of routerinfos successfully fetched
**/ **/
private void reseedOne(String seedURL, boolean echoStatus) { private int reseedOne(String seedURL, boolean echoStatus) {
try { try {
System.setProperty(PROP_ERROR, "");
System.setProperty(PROP_STATUS, _("Reseeding: fetching seed URL.")); System.setProperty(PROP_STATUS, _("Reseeding: fetching seed URL."));
System.err.println("Reseed from " + seedURL); System.err.println("Reseeding from " + seedURL);
URL dir = new URL(seedURL); URL dir = new URL(seedURL);
byte contentRaw[] = readURL(dir); byte contentRaw[] = readURL(dir);
if (contentRaw == null) { if (contentRaw == null) {
System.setProperty(PROP_ERROR,
_("Last reseed failed fully (failed reading seed URL).") + ' ' +
_(RESEED_TIPS));
// Logging deprecated here since attemptFailed() provides better info // Logging deprecated here since attemptFailed() provides better info
_log.debug("Failed reading seed URL: " + seedURL); _log.warn("Failed reading seed URL: " + seedURL);
return; System.err.println("Reseed got no router infos from " + seedURL);
return 0;
} }
String content = new String(contentRaw); String content = new String(contentRaw);
Set<String> urls = new HashSet(1024); Set<String> urls = new HashSet(1024);
@ -173,11 +254,9 @@ public class Reseeder {
cur = end + 1; cur = end + 1;
} }
if (total <= 0) { if (total <= 0) {
_log.error("Read " + contentRaw.length + " bytes from seed " + seedURL + ", but found no routerInfo URLs."); _log.warn("Read " + contentRaw.length + " bytes from seed " + seedURL + ", but found no routerInfo URLs.");
System.setProperty(PROP_ERROR, System.err.println("Reseed got no router infos from " + seedURL);
_("Last reseed failed fully (no routerInfo URLs at seed URL).") + ' ' + return 0;
_(RESEED_TIPS));
return;
} }
List<String> urlList = new ArrayList(urls); List<String> urlList = new ArrayList(urls);
@ -201,32 +280,18 @@ public class Reseeder {
errors++; errors++;
} }
} }
System.err.println("Reseed got " + fetched + " router infos from " + seedURL); System.err.println("Reseed got " + fetched + " router infos from " + seedURL + " with " + errors + " errors");
int failPercent = 100 * errors / total;
// Less than 10% of failures is considered success,
// because some routerInfos will always fail.
if ((failPercent >= 10) && (failPercent < 90)) {
System.setProperty(PROP_ERROR,
_("Last reseed failed partly ({0}% of {1}).", failPercent, total) + ' ' +
_(RESEED_TIPS));
}
if (failPercent >= 90) {
System.setProperty(PROP_ERROR,
_("Last reseed failed ({0}% of {1}).", failPercent, total) + ' ' +
_(RESEED_TIPS));
}
if (fetched > 0) if (fetched > 0)
_context.netDb().rescan(); _context.netDb().rescan();
// Don't go on to the next URL if we have enough // Don't go on to the next URL if we have enough
if (fetched >= 100) if (fetched >= 100)
_isRunning = false; _isRunning = false;
return fetched;
} catch (Throwable t) { } catch (Throwable t) {
System.setProperty(PROP_ERROR, _log.warn("Error reseeding", t);
_("Last reseed failed fully (exception caught).") + ' ' + System.err.println("Reseed got no router infos from " + seedURL);
_(RESEED_TIPS)); return 0;
_log.error("Error reseeding", t);
} }
} }
@ -248,8 +313,17 @@ public class Reseeder {
ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024); ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024);
EepGet get; EepGet get;
if (url.toString().startsWith("https")) { boolean ssl = url.toString().startsWith("https");
get = new SSLEepGet(I2PAppContext.getGlobalContext(), baos, url.toString()); if (ssl) {
SSLEepGet sslget;
if (_sslState == null) {
sslget = new SSLEepGet(I2PAppContext.getGlobalContext(), baos, url.toString());
// save state for next time
_sslState = sslget.getSSLState();
} else {
sslget = new SSLEepGet(I2PAppContext.getGlobalContext(), baos, url.toString(), _sslState);
}
get = sslget;
} else { } else {
// Do a (probably) non-proxied eepget into our ByteArrayOutputStream with 0 retries // Do a (probably) non-proxied eepget into our ByteArrayOutputStream with 0 retries
boolean shouldProxy = _proxyHost != null && _proxyHost.length() > 0 && _proxyPort > 0; boolean shouldProxy = _proxyHost != null && _proxyHost.length() > 0 && _proxyPort > 0;
@ -257,7 +331,9 @@ public class Reseeder {
null, baos, url.toString(), false, null, null); null, baos, url.toString(), false, null, null);
} }
get.addStatusListener(ReseedRunner.this); get.addStatusListener(ReseedRunner.this);
if (get.fetch()) return baos.toByteArray(); else return null; if (get.fetch())
return baos.toByteArray();
return null;
} }
private void writeSeed(String name, byte data[]) throws Exception { private void writeSeed(String name, byte data[]) throws Exception {
@ -295,6 +371,11 @@ public class Reseeder {
return Translate.getString(s, o, o2, _context, BUNDLE_NAME); return Translate.getString(s, o, o2, _context, BUNDLE_NAME);
} }
/** translate */
private String ngettext(String s, String p, int n) {
return Translate.getString(n, s, p, _context, BUNDLE_NAME);
}
/****** /******
public static void main(String args[]) { public static void main(String args[]) {
if ( (args != null) && (args.length == 1) && (!Boolean.valueOf(args[0]).booleanValue()) ) { if ( (args != null) && (args.length == 1) && (!Boolean.valueOf(args[0]).booleanValue()) ) {

View File

@ -1274,10 +1274,10 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
return; return;
} }
} else { } else {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.WARN))
_log.error("CRC incorrect for message " + _messagesRead + " (calc=" + val + " expected=" + _expectedCrc + ") size=" + _size + " blocks " + _blocks); _log.warn("CRC incorrect for message " + _messagesRead + " (calc=" + val + " expected=" + _expectedCrc + ") size=" + _size + " blocks " + _blocks);
_context.statManager().addRateData("ntcp.corruptI2NPCRC", 1, getUptime()); _context.statManager().addRateData("ntcp.corruptI2NPCRC", 1, getUptime());
// should we try to read in the message and keep going? // FIXME should we try to read in the message and keep going?
close(); close();
// databuf is lost // databuf is lost
return; return;