forked from I2P_Developers/i2p.i2p
* HTTP Proxy: Address helper refactoring, address book add form
This commit is contained in:
@ -18,16 +18,20 @@ import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.naming.NamingService;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.client.streaming.I2PSocketOptions;
|
||||
import net.i2p.data.Base32;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.EventDispatcher;
|
||||
@ -62,7 +66,16 @@ import net.i2p.util.Translate;
|
||||
*/
|
||||
public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runnable {
|
||||
|
||||
private HashMap addressHelpers = new HashMap();
|
||||
/**
|
||||
* Map of host name to base64 destination for destinations collected
|
||||
* via address helper links
|
||||
*/
|
||||
private final ConcurrentHashMap<String, String> addressHelpers = new ConcurrentHashMap(8);
|
||||
|
||||
/**
|
||||
* Used to protect actions via http://proxy.i2p/
|
||||
*/
|
||||
private final String _proxyNonce;
|
||||
|
||||
/**
|
||||
* These are backups if the xxx.ht error page is missing.
|
||||
@ -130,6 +143,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"or naming one of them differently.<p>")
|
||||
.getBytes();
|
||||
|
||||
private final static byte[] ERR_AHELPER_NEW =
|
||||
("HTTP/1.1 409 New Address\r\n"+
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n"+
|
||||
"Cache-control: no-cache\r\n"+
|
||||
"\r\n"+
|
||||
"<html><body><H1>New Host Name with Address Helper</H1>"+
|
||||
"The address helper link you followed is for a new host name that is not in your address book. " +
|
||||
"You may either save the destination for this host name to your address book, or remember it only until your router restarts. " +
|
||||
"If you save it to your address book, you will not see this message again. " +
|
||||
"If you do not wish to visit this host, click the \"back\" button on your browser.")
|
||||
.getBytes();
|
||||
|
||||
private final static byte[] ERR_BAD_PROTOCOL =
|
||||
("HTTP/1.1 403 Bad Protocol\r\n"+
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n"+
|
||||
@ -162,6 +187,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
|
||||
public I2PTunnelHTTPClient(int localPort, Logging l, I2PSocketManager sockMgr, I2PTunnel tunnel, EventDispatcher notifyThis, long clientId) {
|
||||
super(localPort, l, sockMgr, tunnel, notifyThis, clientId);
|
||||
_proxyNonce = Long.toString(_context.random().nextLong());
|
||||
// proxyList = new ArrayList();
|
||||
|
||||
setName("HTTP Proxy on " + getTunnel().listenHost + ':' + localPort);
|
||||
@ -177,6 +203,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
String wwwProxy, EventDispatcher notifyThis,
|
||||
I2PTunnel tunnel) throws IllegalArgumentException {
|
||||
super(localPort, ownDest, l, notifyThis, "HTTP Proxy on " + tunnel.listenHost + ':' + localPort + " #" + (++__clientId), tunnel);
|
||||
_proxyNonce = Long.toString(_context.random().nextLong());
|
||||
|
||||
//proxyList = new ArrayList(); // We won't use outside of i2p
|
||||
if (waitEventValue("openBaseClientResult").equals("error")) {
|
||||
@ -258,12 +285,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
return rv;
|
||||
}
|
||||
|
||||
private static final String LOCAL_SERVER = "proxy.i2p";
|
||||
private static final boolean DEFAULT_GZIP = true;
|
||||
// all default to false
|
||||
/** all default to false */
|
||||
public static final String PROP_REFERER = "i2ptunnel.httpclient.sendReferer";
|
||||
public static final String PROP_USER_AGENT = "i2ptunnel.httpclient.sendUserAgent";
|
||||
public static final String PROP_VIA = "i2ptunnel.httpclient.sendVia";
|
||||
public static final String PROP_JUMP_SERVERS = "i2ptunnel.httpclient.jumpServers";
|
||||
public static final String PROP_DISABLE_HELPER = "i2ptunnel.httpclient.disableAddressHelper";
|
||||
|
||||
protected void clientConnectionRun(Socket s) {
|
||||
InputStream in = null;
|
||||
@ -278,7 +307,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
InputReader reader = new InputReader(s.getInputStream());
|
||||
String line, method = null, protocol = null, host = null, destination = null;
|
||||
StringBuilder newRequest = new StringBuilder();
|
||||
int ahelper = 0;
|
||||
boolean ahelperPresent = false;
|
||||
boolean ahelperNew = false;
|
||||
String ahelperKey = null;
|
||||
String userAgent = null;
|
||||
String authorization = null;
|
||||
while ((line = reader.readLine(method)) != null) {
|
||||
line = line.trim();
|
||||
@ -334,6 +366,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
protocol = request.substring(0, pos + 2);
|
||||
request = request.substring(pos + 2);
|
||||
|
||||
// "foo.i2p/bar/baz HTTP/1.1", with any i2paddresshelper parameter removed
|
||||
targetRequest = request;
|
||||
|
||||
// pos is the start of the path
|
||||
@ -380,21 +413,20 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
destination = host;
|
||||
host = getHostName(destination);
|
||||
line = method + ' ' + request.substring(pos);
|
||||
} else if (host.toLowerCase().equals("proxy.i2p")) {
|
||||
} else if (host.toLowerCase().equals(LOCAL_SERVER)) {
|
||||
// so we don't do any naming service lookups
|
||||
destination = host;
|
||||
usingInternalServer = true;
|
||||
} else if (host.toLowerCase().endsWith(".i2p")) {
|
||||
// Destination gets the host name
|
||||
destination = host;
|
||||
// Host becomes the destination key
|
||||
// Host becomes the destination's "{b32}.b32.i2p" string, or "i2p" on lookup failure
|
||||
host = getHostName(destination);
|
||||
|
||||
int pos2;
|
||||
if ((pos2 = request.indexOf("?")) != -1) {
|
||||
// Try to find an address helper in the fragments
|
||||
// and split the request into it's component parts for rebuilding later
|
||||
String ahelperKey = null;
|
||||
boolean ahelperConflict = false;
|
||||
|
||||
String fragments = request.substring(pos2 + 1);
|
||||
@ -404,6 +436,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
String urlEncoding = "";
|
||||
fragments = fragments.substring(0, pos2);
|
||||
String initialFragments = fragments;
|
||||
// FIXME split on ';' also
|
||||
fragments = fragments + "&";
|
||||
String fragment;
|
||||
while(fragments.length() > 0) {
|
||||
@ -412,35 +445,43 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
fragments = fragments.substring(pos2 + 1);
|
||||
|
||||
// Fragment looks like addresshelper key
|
||||
if (fragment.startsWith("i2paddresshelper=")) {
|
||||
if (fragment.startsWith("i2paddresshelper=") &&
|
||||
!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
|
||||
pos2 = fragment.indexOf("=");
|
||||
ahelperKey = fragment.substring(pos2 + 1);
|
||||
|
||||
// Key contains data, lets not ignore it
|
||||
if (ahelperKey != null) {
|
||||
ahelperPresent = true;
|
||||
// ahelperKey will be validated later
|
||||
|
||||
// Host resolvable only with addresshelper
|
||||
if ( (host == null) || ("i2p".equals(host)) )
|
||||
{
|
||||
// Cannot check, use addresshelper key
|
||||
addressHelpers.put(destination,ahelperKey);
|
||||
} else {
|
||||
// Host resolvable from database, verify addresshelper key
|
||||
// Silently bypass correct keys, otherwise alert
|
||||
String destB64 = null;
|
||||
Destination _dest = _context.namingService().lookup(host);
|
||||
if (_dest != null)
|
||||
destB64 = _dest.toBase64();
|
||||
if (destB64 != null && !destB64.equals(ahelperKey))
|
||||
{
|
||||
if (host == null || "i2p".equals(host)) {
|
||||
// Host lookup failed - resolvable only with addresshelper
|
||||
// Store in local HashMap unless there is conflict
|
||||
String old = addressHelpers.putIfAbsent(destination.toLowerCase(), ahelperKey);
|
||||
ahelperNew = old == null;
|
||||
if ((!ahelperNew) && !old.equals(ahelperKey)) {
|
||||
// Conflict: handle when URL reconstruction done
|
||||
ahelperConflict = true;
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination + "], trusted key [" + destB64 + "], specified key [" + ahelperKey + "].");
|
||||
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination +
|
||||
"], trusted key [" + old + "], specified key [" + ahelperKey + "].");
|
||||
}
|
||||
} else {
|
||||
// If the host is resolvable from database, verify addresshelper key
|
||||
// Silently bypass correct keys, otherwise alert
|
||||
Destination hostDest = _context.namingService().lookup(destination);
|
||||
if (hostDest != null) {
|
||||
String destB64 = hostDest.toBase64();
|
||||
if (destB64 != null && !destB64.equals(ahelperKey)) {
|
||||
// Conflict: handle when URL reconstruction done
|
||||
ahelperConflict = true;
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination +
|
||||
"], trusted key [" + destB64 + "], specified key [" + ahelperKey + "].");
|
||||
}
|
||||
}
|
||||
}
|
||||
} // ahelperKey
|
||||
} else {
|
||||
// Other fragments, just pass along
|
||||
// Append each fragment to urlEncoding
|
||||
@ -453,11 +494,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
}
|
||||
// Reconstruct the request minus the i2paddresshelper GET var
|
||||
request = uriPath + urlEncoding + " " + protocolVersion;
|
||||
targetRequest = request;
|
||||
|
||||
// Did addresshelper key conflict?
|
||||
if (ahelperConflict)
|
||||
{
|
||||
|
||||
if (ahelperConflict) {
|
||||
if (out != null) {
|
||||
// convert ahelperKey to b32
|
||||
String alias = getHostName(ahelperKey);
|
||||
@ -479,16 +519,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
s.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // end query processing
|
||||
|
||||
String addressHelper = (String) addressHelpers.get(destination);
|
||||
if (addressHelper != null) {
|
||||
destination = addressHelper;
|
||||
host = getHostName(destination);
|
||||
ahelper = 1;
|
||||
}
|
||||
String addressHelper = addressHelpers.get(destination);
|
||||
if (addressHelper != null)
|
||||
host = getHostName(addressHelper);
|
||||
|
||||
line = method + " " + request.substring(pos);
|
||||
// end of (host endsWith(".i2p"))
|
||||
|
||||
} else if (host.toLowerCase().equals("localhost") || host.equals("127.0.0.1") ||
|
||||
host.startsWith("192.168.")) {
|
||||
// if somebody is trying to get to 192.168.example.com, oh well
|
||||
@ -584,10 +623,13 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
line = "Host: " + host;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix(requestId) + "Setting host = " + host);
|
||||
} else if (lowercaseLine.startsWith("user-agent: ") &&
|
||||
!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_USER_AGENT)).booleanValue()) {
|
||||
} else if (lowercaseLine.startsWith("user-agent: ")) {
|
||||
// save for deciding whether to offer address book form
|
||||
userAgent = lowercaseLine.substring(12);
|
||||
if (!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_USER_AGENT)).booleanValue()) {
|
||||
line = null;
|
||||
continue;
|
||||
}
|
||||
} else if (lowercaseLine.startsWith("accept")) {
|
||||
// strip the accept-blah headers, as they vary dramatically from
|
||||
// browser to browser
|
||||
@ -687,15 +729,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix(requestId) + "Destination: " + destination);
|
||||
|
||||
// Serve local proxy files (images, css linked from error pages)
|
||||
// Ignore all the headers
|
||||
// Allow without authorization
|
||||
if (usingInternalServer) {
|
||||
serveLocalFile(out, method, targetRequest);
|
||||
s.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Authorization
|
||||
if (!authorize(s, requestId, authorization)) {
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
@ -705,19 +738,41 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
_log.warn(getPrefix(requestId) + "Auth required, sending 407");
|
||||
}
|
||||
out.write(getErrorPage("auth", ERR_AUTH));
|
||||
writeFooter(out);
|
||||
s.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Serve local proxy files (images, css linked from error pages)
|
||||
// Ignore all the headers
|
||||
if (usingInternalServer) {
|
||||
// disable the add form if address helper is disabled
|
||||
if (targetRequest.startsWith(LOCAL_SERVER + "/add?") &&
|
||||
Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
|
||||
out.write(ERR_HELPER_DISABLED);
|
||||
} else {
|
||||
serveLocalFile(out, method, targetRequest, _proxyNonce);
|
||||
}
|
||||
s.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// LOOKUP
|
||||
// If the host is "i2p", the getHostName() lookup failed, don't try to
|
||||
// look it up again as the naming service does not do negative caching
|
||||
// so it will be slow.
|
||||
|
||||
Destination clientDest;
|
||||
if ("i2p".equals(host))
|
||||
String addressHelper = addressHelpers.get(destination.toLowerCase());
|
||||
if (addressHelper != null) {
|
||||
clientDest = _context.namingService().lookup(addressHelper);
|
||||
// remove bad entries
|
||||
if (clientDest == null)
|
||||
addressHelpers.remove(destination.toLowerCase());
|
||||
} else if ("i2p".equals(host)) {
|
||||
clientDest = null;
|
||||
else
|
||||
} else {
|
||||
clientDest = _context.namingService().lookup(destination);
|
||||
}
|
||||
|
||||
if (clientDest == null) {
|
||||
//l.log("Could not resolve " + destination + ".");
|
||||
@ -727,7 +782,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
String jumpServers = null;
|
||||
if (usingWWWProxy)
|
||||
header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN);
|
||||
else if(ahelper != 0)
|
||||
else if (ahelperPresent)
|
||||
header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
|
||||
else if (destination.length() == 60 && destination.endsWith(".b32.i2p"))
|
||||
header = getErrorPage("dnf", ERR_DESTINATION_UNKNOWN);
|
||||
@ -741,7 +796,34 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
s.close();
|
||||
return;
|
||||
}
|
||||
String remoteID;
|
||||
|
||||
// Address helper response form
|
||||
// This will only load once - the second time it won't be "new"
|
||||
// Don't do this for eepget, which uses a user-agent of "Wget"
|
||||
if (ahelperNew && "GET".equals(method) &&
|
||||
(userAgent == null || !userAgent.startsWith("Wget")) &&
|
||||
!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
|
||||
writeHelperSaveForm(out, destination, ahelperKey, protocol + targetRequest);
|
||||
s.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Redirect to non-addresshelper URL to not clog the browser address bar
|
||||
// and not pass the parameter to the eepsite.
|
||||
// This also prevents the not-found error page from looking bad
|
||||
if (ahelperPresent) {
|
||||
String uri = protocol + targetRequest;
|
||||
int spc = uri.indexOf(" ");
|
||||
if (spc >= 0)
|
||||
uri = uri.substring(0, spc);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Auto redirecting to " + uri);
|
||||
out.write(("HTTP/1.1 301 Address Helper Accepted\r\n"+
|
||||
"Location: " + uri + "\r\n"+
|
||||
"\r\n").getBytes("UTF-8"));
|
||||
s.close();
|
||||
return;
|
||||
}
|
||||
|
||||
Properties opts = new Properties();
|
||||
//opts.setProperty("i2p.streaming.inactivityTimeout", ""+120*1000);
|
||||
@ -779,6 +861,38 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 0.8.7 */
|
||||
private void writeHelperSaveForm(OutputStream out, String destination, String ahelperKey, String targetRequest) throws IOException {
|
||||
if (out == null)
|
||||
return;
|
||||
// strip HTTP/1.1
|
||||
int protopos = targetRequest.indexOf(" ");
|
||||
if (protopos >= 0)
|
||||
targetRequest = targetRequest.substring(0, protopos);
|
||||
byte[] header = getErrorPage("ahelper-new", ERR_AHELPER_NEW);
|
||||
out.write(header);
|
||||
out.write(("<table><tr><td class=\"mediumtags\" align=\"right\">" + _("Host") + "</td><td class=\"mediumtags\">" + destination + "</td></tr>\n" +
|
||||
"<tr><td class=\"mediumtags\" align=\"right\">" + _("Destination") + "</td><td>" +
|
||||
"<textarea rows=\"1\" style=\"height: 4em; min-width: 0; min-height: 0;\" cols=\"70\" wrap=\"off\" readonly=\"readonly\" >" +
|
||||
ahelperKey + "</textarea></td></tr></table>\n" +
|
||||
"<hr><div class=\"formaction\">"+
|
||||
"<form method=\"GET\" action=\"" + targetRequest + "\">" +
|
||||
"<button type=\"submit\">" + _("Continue to {0} without saving", destination) + "</button>" +
|
||||
"</form>\n<form method=\"GET\" action=\"http://" + LOCAL_SERVER + "/add\">" +
|
||||
"<input type=\"hidden\" name=\"host\" value=\"" + destination + "\">\n" +
|
||||
"<input type=\"hidden\" name=\"dest\" value=\"" + ahelperKey + "\">\n" +
|
||||
"<input type=\"hidden\" name=\"nonce\" value=\"" + _proxyNonce + "\">\n" +
|
||||
"<button type=\"submit\" name=\"router\" value=\"router\">" + _("Save {0} to router address book and continue to eepsite", destination) + "</button><br>\n").getBytes("UTF-8"));
|
||||
if (_context.namingService().getName().equals("BlockfileNamingService")) {
|
||||
// only blockfile supports multiple books
|
||||
out.write(("<button type=\"submit\" name=\"master\" value=\"master\">" + _("Save {0} to master address book and continue to eepsite", destination) + "</button><br>\n").getBytes("UTF-8"));
|
||||
out.write(("<button type=\"submit\" name=\"private\" value=\"private\">" + _("Save {0} to private address book and continue to eepsite", destination) + "</button>\n").getBytes("UTF-8"));
|
||||
}
|
||||
out.write(("<input type=\"hidden\" name=\"url\" value=\"" + targetRequest + "\">\n" +
|
||||
"</form></div></div>").getBytes());
|
||||
writeFooter(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the first line unbuffered.
|
||||
* After that, switch to a BufferedReader, unless the method is "POST".
|
||||
@ -915,7 +1029,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
out.write(errMessage);
|
||||
if (targetRequest != null) {
|
||||
int protopos = targetRequest.indexOf(" ");
|
||||
String uri = null;
|
||||
String uri;
|
||||
if (protopos >= 0)
|
||||
uri = targetRequest.substring(0, protopos);
|
||||
else
|
||||
@ -1022,6 +1136,20 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"HTTP Proxy local file not found")
|
||||
.getBytes();
|
||||
|
||||
private final static byte[] ERR_ADD =
|
||||
("HTTP/1.1 409 Bad\r\n"+
|
||||
"Content-Type: text/plain\r\n"+
|
||||
"\r\n"+
|
||||
"Add to addressbook failed - bad parameters")
|
||||
.getBytes();
|
||||
|
||||
private final static byte[] ERR_HELPER_DISABLED =
|
||||
("HTTP/1.1 403 Disabled\r\n"+
|
||||
"Content-Type: text/plain\r\n"+
|
||||
"\r\n"+
|
||||
"Address helpers disabled")
|
||||
.getBytes();
|
||||
|
||||
/**
|
||||
* Very simple web server.
|
||||
*
|
||||
@ -1043,9 +1171,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
*
|
||||
* @param targetRequest "proxy.i2p/themes/foo.png HTTP/1.1"
|
||||
*/
|
||||
private static void serveLocalFile(OutputStream out, String method, String targetRequest) {
|
||||
private static void serveLocalFile(OutputStream out, String method, String targetRequest, String proxyNonce) {
|
||||
//System.err.println("targetRequest: \"" + targetRequest + "\"");
|
||||
// a home page message for the curious...
|
||||
if (targetRequest.startsWith("proxy.i2p/ ")) {
|
||||
if (targetRequest.startsWith(LOCAL_SERVER + "/ ")) {
|
||||
try {
|
||||
out.write(("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nCache-Control: max-age=86400\r\n\r\nI2P HTTP proxy OK").getBytes());
|
||||
out.flush();
|
||||
@ -1053,12 +1182,12 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
return;
|
||||
}
|
||||
if ((method.equals("GET") || method.equals("HEAD")) &&
|
||||
targetRequest.startsWith("proxy.i2p/themes/") &&
|
||||
targetRequest.startsWith(LOCAL_SERVER + "/themes/") &&
|
||||
!targetRequest.contains("..")) {
|
||||
int space = targetRequest.indexOf(' ');
|
||||
String filename = null;
|
||||
try {
|
||||
filename = targetRequest.substring(17, space); // "proxy.i2p/themes/".length
|
||||
filename = targetRequest.substring(LOCAL_SERVER.length() + 8, space); // "/themes/".length
|
||||
} catch (IndexOutOfBoundsException ioobe) {}
|
||||
// theme hack
|
||||
if (filename.startsWith("console/default/"))
|
||||
@ -1081,9 +1210,65 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
out.write(type.getBytes());
|
||||
out.write("\r\nCache-Control: max-age=86400\r\n\r\n".getBytes());
|
||||
FileUtil.readFile(filename, themesDir.getAbsolutePath(), out);
|
||||
} catch (IOException ioe) {}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add to addressbook (form submit)
|
||||
// Parameters are url, host, dest, nonce, and master | router | private.
|
||||
// Do the add and redirect.
|
||||
if (targetRequest.startsWith(LOCAL_SERVER + "/add?")) {
|
||||
int spc = targetRequest.indexOf(' ');
|
||||
String query = targetRequest.substring(LOCAL_SERVER.length() + 5, spc); // "/add?".length()
|
||||
Map<String, String> opts = new HashMap(8);
|
||||
StringTokenizer tok = new StringTokenizer(query, "=&;");
|
||||
while (tok.hasMoreTokens()) {
|
||||
String k = tok.nextToken();
|
||||
if (!tok.hasMoreTokens())
|
||||
break;
|
||||
String v = tok.nextToken();
|
||||
opts.put(decode(k), decode(v));
|
||||
}
|
||||
|
||||
String url = opts.get("url");
|
||||
String host = opts.get("host");
|
||||
String b64Dest = opts.get("dest");
|
||||
String nonce = opts.get("nonce");
|
||||
String book = "privatehosts.txt";
|
||||
if (opts.get("master") != null)
|
||||
book = "userhosts.txt";
|
||||
else if (opts.get("router") != null)
|
||||
book = "hosts.txt";
|
||||
Destination dest = null;
|
||||
if (b64Dest != null) {
|
||||
try {
|
||||
dest = new Destination(b64Dest);
|
||||
} catch (DataFormatException dfe) {
|
||||
System.err.println("Bad dest to save?" + b64Dest);
|
||||
}
|
||||
}
|
||||
//System.err.println("url : \"" + url + "\"");
|
||||
//System.err.println("host : \"" + host + "\"");
|
||||
//System.err.println("b64dest : \"" + b64Dest + "\"");
|
||||
//System.err.println("book : \"" + book + "\"");
|
||||
//System.err.println("nonce : \"" + nonce + "\"");
|
||||
if (proxyNonce.equals(nonce) && url != null && host != null && dest != null) {
|
||||
try {
|
||||
NamingService ns = I2PAppContext.getGlobalContext().namingService();
|
||||
Properties nsOptions = new Properties();
|
||||
nsOptions.setProperty("list", book);
|
||||
nsOptions.setProperty("s", _("Added via address helper"));
|
||||
boolean success = ns.put(host, dest, nsOptions);
|
||||
writeRedirectPage(out, success, host, book, url);
|
||||
return;
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
try {
|
||||
out.write(ERR_ADD);
|
||||
out.flush();
|
||||
} catch (IOException ioe) {}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
out.write(ERR_404);
|
||||
@ -1091,16 +1276,68 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
|
||||
/** @since 0.8.7 */
|
||||
private static void writeRedirectPage(OutputStream out, boolean success, String host, String book, String url) throws IOException {
|
||||
out.write(("HTTP/1.1 200 OK\r\n"+
|
||||
"Content-Type: text/html; charset=UTF-8\r\n"+
|
||||
"\r\n"+
|
||||
"<html><head>"+
|
||||
"<title>" + _("Redirecting to {0}", host) + "</title>\n" +
|
||||
"<link rel=\"shortcut icon\" href=\"http://proxy.i2p/themes/console/images/favicon.ico\" >\n" +
|
||||
"<link href=\"http://proxy.i2p/themes/console/default/console.css\" rel=\"stylesheet\" type=\"text/css\" >\n" +
|
||||
"<meta http-equiv=\"Refresh\" content=\"1; url=" + url + "\">\n" +
|
||||
"</head><body>\n" +
|
||||
"<div class=logo>\n" +
|
||||
"<a href=\"http://127.0.0.1:7657/\" title=\"" + _("Router Console") + "\"><img src=\"http://proxy.i2p/themes/console/images/i2plogo.png\" alt=\"I2P Router Console\" border=\"0\"></a><hr>\n" +
|
||||
"<a href=\"http://127.0.0.1:7657/config\">" + _("Configuration") + "</a> <a href=\"http://127.0.0.1:7657/help.jsp\">" + _("Help") + "</a> <a href=\"http://127.0.0.1:7657/susidns/index.jsp\">" + _("Addressbook") + "</a>\n" +
|
||||
"</div>" +
|
||||
"<div class=warning id=warning>\n" +
|
||||
"<h3>" +
|
||||
(success ?
|
||||
_("Saved {0} to the {1} addressbook, redirecting now.", host, book) :
|
||||
_("Failed to save {0} to the {1} addressbook, redirecting now.", host, book)) +
|
||||
"</h3>\n<p><a href=\"" + url + "\">" +
|
||||
_("Click here if you are not redirected automatically.") +
|
||||
"</a></p></div>").getBytes("UTF-8"));
|
||||
writeFooter(out);
|
||||
out.flush();
|
||||
}
|
||||
|
||||
private static String decode(String s) {
|
||||
if (!s.contains("%"))
|
||||
return s;
|
||||
StringBuilder buf = new StringBuilder(s.length());
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c != '%') {
|
||||
buf.append(c);
|
||||
} else {
|
||||
try {
|
||||
buf.append((char) Integer.parseInt(s.substring(++i, (++i) + 1), 16));
|
||||
} catch (IndexOutOfBoundsException ioobe) {
|
||||
break;
|
||||
} catch (NumberFormatException nfe) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.web.messages";
|
||||
|
||||
/** lang in routerconsole.lang property, else current locale */
|
||||
public static String _(String key) {
|
||||
protected static String _(String key) {
|
||||
return Translate.getString(key, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
|
||||
}
|
||||
|
||||
/** {0} */
|
||||
protected static String _(String key, Object o) {
|
||||
return Translate.getString(key, o, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
|
||||
}
|
||||
|
||||
/** {0} and {1} */
|
||||
public static String _(String key, Object o, Object o2) {
|
||||
protected static String _(String key, Object o, Object o2) {
|
||||
return Translate.getString(key, o, o2, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,9 @@
|
||||
2011-05-25 zzz
|
||||
* CPUID: Load 64-bit libcpuid if available
|
||||
* HTTP Proxy: Address helper refactoring, address book add form
|
||||
* Naming: B32 fixes
|
||||
* NetDB: Increase floodfills again
|
||||
|
||||
2011-05-23 zzz
|
||||
* Console:
|
||||
- Disable zh translation in graphs on windows due to font issues
|
||||
|
25
installer/resources/proxy/ahelper-new-header.ht
Normal file
25
installer/resources/proxy/ahelper-new-header.ht
Normal file
@ -0,0 +1,25 @@
|
||||
HTTP/1.1 409 New Address
|
||||
Content-Type: text/html; charset=UTF-8
|
||||
Cache-control: no-cache
|
||||
Connection: close
|
||||
Proxy-Connection: close
|
||||
|
||||
<html><head>
|
||||
<title>I2P Information: New Host Name</title>
|
||||
<link rel="shortcut icon" href="http://proxy.i2p/themes/console/images/favicon.ico" >
|
||||
<link href="http://proxy.i2p/themes/console/default/console.css" rel="stylesheet" type="text/css" >
|
||||
</head>
|
||||
<body>
|
||||
<div class=logo>
|
||||
<a href="http://127.0.0.1:7657/index.jsp" title="Router Console"><img src="http://proxy.i2p/themes/console/images/i2plogo.png" alt="I2P Router Console" border="0"></a><hr>
|
||||
<a href="http://127.0.0.1:7657/config.jsp">Configuration</a> <a href="http://127.0.0.1:7657/help.jsp">Help</a> <a href="http://127.0.0.1:7657/susidns/">Addressbook</a>
|
||||
</div>
|
||||
<div class=warning id=warning>
|
||||
<h3>Information: New Host Name with Address Helper</h3>
|
||||
<p>
|
||||
The address helper link you followed is for a new host name that is not in your address book.
|
||||
You may save this host name to your local address book.
|
||||
If you save it to your address book, you will not see this message again.
|
||||
If you do not save it, the host name will be forgotten after the next router restart.
|
||||
If you do not wish to visit this host, click the "back" button on your browser.
|
||||
</p>
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
public final static String VERSION = CoreVersion.VERSION;
|
||||
public final static long BUILD = 5;
|
||||
public final static long BUILD = 6;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
Reference in New Issue
Block a user