forked from I2P_Developers/i2p.i2p
allow SHOUTcast/icecast to work over the http proxy + cleanups
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -74,25 +73,20 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
* 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.
|
||||
*/
|
||||
|
||||
private final static byte[] ERR_REQUEST_DENIED =
|
||||
("HTTP/1.1 403 Access Denied\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
"Cache-control: no-cache\r\n" +
|
||||
"\r\n" +
|
||||
"<html><body><H1>I2P ERROR: REQUEST DENIED</H1>" +
|
||||
"You attempted to connect to a non-I2P website or location.<BR>")
|
||||
.getBytes();
|
||||
|
||||
"You attempted to connect to a non-I2P website or location.<BR>").getBytes();
|
||||
private final static byte[] ERR_DESTINATION_UNKNOWN =
|
||||
("HTTP/1.1 503 Service Unavailable\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
@@ -103,9 +97,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"wrong BASE64 I2P Destination or the link you are following is " +
|
||||
"bad. The host (or the WWW proxy, if you're using one) could also " +
|
||||
"be temporarily offline. You may want to <b>retry</b>. " +
|
||||
"Could not find the following Destination:<BR><BR><div>")
|
||||
.getBytes();
|
||||
|
||||
"Could not find the following Destination:<BR><BR><div>").getBytes();
|
||||
/*****
|
||||
private final static byte[] ERR_TIMEOUT =
|
||||
("HTTP/1.1 504 Gateway Timeout\r\n"+
|
||||
@@ -119,17 +111,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"the following Destination:<BR><BR>")
|
||||
.getBytes();
|
||||
*****/
|
||||
|
||||
private final static byte[] ERR_NO_OUTPROXY =
|
||||
private final static byte[] _ERR_NO_OUTPROXY =
|
||||
("HTTP/1.1 503 Service Unavailable\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
"Cache-control: no-cache\r\n" +
|
||||
"\r\n" +
|
||||
"<html><body><H1>I2P ERROR: No outproxy found</H1>" +
|
||||
"Your request was for a site outside of I2P, but you have no " +
|
||||
"HTTP outproxy configured. Please configure an outproxy in I2PTunnel")
|
||||
.getBytes();
|
||||
|
||||
"HTTP outproxy configured. Please configure an outproxy in I2PTunnel").getBytes();
|
||||
private final static byte[] ERR_AHELPER_CONFLICT =
|
||||
("HTTP/1.1 409 Conflict\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
@@ -143,9 +132,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"You can resolve the conflict by considering which key you trust, " +
|
||||
"and either discarding the addresshelper link, " +
|
||||
"discarding the host entry from your host database, " +
|
||||
"or naming one of them differently.<p>")
|
||||
.getBytes();
|
||||
|
||||
"or naming one of them differently.<p>").getBytes();
|
||||
private final static byte[] ERR_AHELPER_NOTFOUND =
|
||||
("HTTP/1.1 404 Not Found\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
@@ -154,9 +141,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"<html><body><H1>I2P ERROR: Helper key not resolvable.</H1>" +
|
||||
"The helper key you put for i2paddresshelper= is not resolvable. " +
|
||||
"It seems to be garbage data, or a mistyped b32. Check your URL " +
|
||||
"to try and fix the helper key to be either a b32 or a base64.")
|
||||
.getBytes();
|
||||
|
||||
"to try and fix the helper key to be either a b32 or a base64.").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" +
|
||||
@@ -166,9 +151,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"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();
|
||||
|
||||
"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" +
|
||||
@@ -176,18 +159,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"\r\n" +
|
||||
"<html><body><H1>I2P ERROR: NON-HTTP PROTOCOL</H1>" +
|
||||
"The request uses a bad protocol. " +
|
||||
"The I2P HTTP Proxy supports http:// requests ONLY. Other protocols such as https:// and ftp:// are not allowed.<BR>")
|
||||
.getBytes();
|
||||
|
||||
"The I2P HTTP Proxy supports http:// requests ONLY. Other protocols such as https:// and ftp:// are not allowed.<BR>").getBytes();
|
||||
private final static byte[] ERR_LOCALHOST =
|
||||
("HTTP/1.1 403 Access Denied\r\n" +
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n" +
|
||||
"Cache-control: no-cache\r\n" +
|
||||
"\r\n" +
|
||||
"<html><body><H1>I2P ERROR: REQUEST DENIED</H1>" +
|
||||
"Your browser is misconfigured. Do not use the proxy to access the router console or other localhost destinations.<BR>")
|
||||
.getBytes();
|
||||
|
||||
"Your browser is misconfigured. Do not use the proxy to access the router console or other localhost destinations.<BR>").getBytes();
|
||||
private final static byte[] ERR_AUTH =
|
||||
("HTTP/1.1 407 Proxy Authentication Required\r\n" +
|
||||
"Content-Type: text/html; charset=UTF-8\r\n" +
|
||||
@@ -196,8 +175,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
"Proxy-Authenticate: Basic realm=\"I2P HTTP Proxy\"\r\n" +
|
||||
"\r\n" +
|
||||
"<html><body><H1>I2P ERROR: PROXY AUTHENTICATION REQUIRED</H1>" +
|
||||
"This proxy is configured to require authentication.<BR>")
|
||||
.getBytes();
|
||||
"This proxy is configured to require authentication.<BR>").getBytes();
|
||||
|
||||
/**
|
||||
* This constructor always starts the tunnel (ignoring the i2cp.delayOpen option).
|
||||
@@ -215,6 +193,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
|
||||
notifyEvent("openHTTPClientResult", "ok");
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException if the I2PTunnel does not contain
|
||||
* valid config to contact the router
|
||||
@@ -233,9 +212,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
|
||||
if(wwwProxy != null) {
|
||||
StringTokenizer tok = new StringTokenizer(wwwProxy, ", ");
|
||||
while (tok.hasMoreTokens())
|
||||
while(tok.hasMoreTokens()) {
|
||||
_proxyList.add(tok.nextToken().trim());
|
||||
}
|
||||
}
|
||||
|
||||
setName("HTTP Proxy on " + tunnel.listenHost + ':' + localPort);
|
||||
|
||||
@@ -251,13 +231,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
@Override
|
||||
protected I2PSocketOptions getDefaultOptions() {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
|
||||
if(!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT)) {
|
||||
defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, "" + DEFAULT_READ_TIMEOUT);
|
||||
}
|
||||
//if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
|
||||
// defaultOpts.setProperty("i2p.streaming.inactivityTimeout", ""+DEFAULT_READ_TIMEOUT);
|
||||
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
|
||||
if(!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT)) {
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
}
|
||||
return opts;
|
||||
}
|
||||
|
||||
@@ -269,18 +251,20 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
protected I2PSocketOptions getDefaultOptions(Properties overrides) {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
defaultOpts.putAll(overrides);
|
||||
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
|
||||
if(!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT)) {
|
||||
defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, "" + DEFAULT_READ_TIMEOUT);
|
||||
if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
|
||||
}
|
||||
if(!defaultOpts.contains("i2p.streaming.inactivityTimeout")) {
|
||||
defaultOpts.setProperty("i2p.streaming.inactivityTimeout", "" + DEFAULT_READ_TIMEOUT);
|
||||
}
|
||||
// delayed start
|
||||
verifySocketManager();
|
||||
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
|
||||
if(!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT)) {
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
}
|
||||
return opts;
|
||||
}
|
||||
|
||||
private InternalSocketRunner isr;
|
||||
|
||||
/**
|
||||
@@ -301,14 +285,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
@Override
|
||||
public boolean close(boolean forced) {
|
||||
int reg = _context.portMapper().getPort(PortMapper.SVC_HTTP_PROXY);
|
||||
if (reg == getLocalPort())
|
||||
if(reg == getLocalPort()) {
|
||||
_context.portMapper().unregister(PortMapper.SVC_HTTP_PROXY);
|
||||
}
|
||||
boolean rv = super.close(forced);
|
||||
if (this.isr != null)
|
||||
if(this.isr != null) {
|
||||
this.isr.stopRunning();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
private static final String HELPER_PARAM = "i2paddresshelper";
|
||||
public static final String LOCAL_SERVER = "proxy.i2p";
|
||||
private static final boolean DEFAULT_GZIP = true;
|
||||
@@ -333,6 +318,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
String internalRawQuery = null;
|
||||
String currentProxy = null;
|
||||
long requestId = ++__requestId;
|
||||
boolean shout = false;
|
||||
|
||||
try {
|
||||
out = s.getOutputStream();
|
||||
@@ -346,22 +332,26 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
String authorization = null;
|
||||
while((line = reader.readLine(method)) != null) {
|
||||
line = line.trim();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
if(_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug(getPrefix(requestId) + "Line=[" + line + "]");
|
||||
}
|
||||
|
||||
String lowercaseLine = line.toLowerCase(Locale.US);
|
||||
if(lowercaseLine.startsWith("connection: ") ||
|
||||
lowercaseLine.startsWith("keep-alive: ") ||
|
||||
lowercaseLine.startsWith("proxy-connection: "))
|
||||
lowercaseLine.startsWith("proxy-connection: ")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(method == null) { // first line (GET /base64/realaddr)
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
if(_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug(getPrefix(requestId) + "First line [" + line + "]");
|
||||
}
|
||||
|
||||
String[] params = line.split(" ", 3);
|
||||
if (params.length != 3)
|
||||
if(params.length != 3) {
|
||||
break;
|
||||
}
|
||||
String request = params[1];
|
||||
|
||||
// various obscure fixups
|
||||
@@ -372,8 +362,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
// Deprecated
|
||||
// /eepproxy/foo.i2p/bar/baz.html
|
||||
String subRequest = request.substring("/eepproxy/".length());
|
||||
if (subRequest.indexOf("/") == -1)
|
||||
if(subRequest.indexOf("/") == -1) {
|
||||
subRequest += "/";
|
||||
}
|
||||
request = "http://" + subRequest;
|
||||
/****
|
||||
} else if (request.toLowerCase(Locale.US).startsWith("http://i2p/")) {
|
||||
@@ -410,19 +401,22 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
requestURI = new URI(request);
|
||||
if(requestURI.getRawUserInfo() != null || requestURI.getRawFragment() != null) {
|
||||
// these should never be sent to the proxy in the request line
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
if(_log.shouldLog(Log.WARN)) {
|
||||
_log.warn(getPrefix(requestId) + "Removing userinfo or fragment [" + request + "]");
|
||||
}
|
||||
requestURI = changeURI(requestURI, null, 0, null);
|
||||
}
|
||||
if(requestURI.getPath() == null || requestURI.getPath().length() <= 0) {
|
||||
// Add a path
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
if(_log.shouldLog(Log.WARN)) {
|
||||
_log.warn(getPrefix(requestId) + "Adding / path to [" + request + "]");
|
||||
}
|
||||
requestURI = changeURI(requestURI, null, 0, "/");
|
||||
}
|
||||
} catch(URISyntaxException use) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
if(_log.shouldLog(Log.WARN)) {
|
||||
_log.warn(getPrefix(requestId) + "Bad request [" + request + "]", use);
|
||||
}
|
||||
break;
|
||||
}
|
||||
method = params[0];
|
||||
@@ -454,24 +448,25 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
internalPath = requestURI.getPath();
|
||||
internalRawQuery = requestURI.getRawQuery();
|
||||
} else if(hostLowerCase.equals("i2p")) {
|
||||
// pull the b64 dest out of the first path element
|
||||
// pull the b64 _dest out of the first path element
|
||||
String oldPath = requestURI.getPath().substring(1);
|
||||
int slash = oldPath.indexOf("/");
|
||||
if(slash < 0) {
|
||||
slash = oldPath.length();
|
||||
oldPath += "/";
|
||||
}
|
||||
String dest = oldPath.substring(0, slash);
|
||||
if (slash >= 516 && !dest.contains(".")) {
|
||||
String _dest = oldPath.substring(0, slash);
|
||||
if(slash >= 516 && !_dest.contains(".")) {
|
||||
// possible alternative:
|
||||
// redirect to b32
|
||||
destination = dest;
|
||||
destination = _dest;
|
||||
host = getHostName(destination);
|
||||
targetRequest = requestURI.toASCIIString();
|
||||
String newURI = oldPath.substring(slash);
|
||||
String query = requestURI.getRawQuery();
|
||||
if (query != null)
|
||||
if(query != null) {
|
||||
newURI += '?' + query;
|
||||
}
|
||||
try {
|
||||
requestURI = new URI(newURI);
|
||||
} catch(URISyntaxException use) {
|
||||
@@ -495,8 +490,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
// TODO support I2P ports someday
|
||||
//if (port >= 0)
|
||||
// host = host + ':' + port;
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
if(_log.shouldLog(Log.WARN)) {
|
||||
_log.warn(getPrefix(requestId) + "Removing port from [" + request + "]");
|
||||
}
|
||||
try {
|
||||
requestURI = changeURI(requestURI, null, -1, null);
|
||||
} catch(URISyntaxException use) {
|
||||
@@ -515,8 +511,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
if(helperStrings != null &&
|
||||
!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
|
||||
query = helperStrings[0];
|
||||
if (query.equals(""))
|
||||
if(query.equals("")) {
|
||||
query = null;
|
||||
}
|
||||
try {
|
||||
requestURI = replaceQuery(requestURI, query);
|
||||
} catch(URISyntaxException use) {
|
||||
@@ -535,10 +532,11 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
i.e. on your eepsite put
|
||||
<a href="?i2paddresshelper=name.i2p">This is the name I want to be called.</a>
|
||||
*/
|
||||
Destination dest = _context.namingService().lookup(ahelperKey);
|
||||
if(dest==null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
Destination _dest = _context.namingService().lookup(ahelperKey);
|
||||
if(_dest == null) {
|
||||
if(_log.shouldLog(Log.WARN)) {
|
||||
_log.warn(getPrefix(requestId) + "Could not find destination for " + ahelperKey);
|
||||
}
|
||||
byte[] header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
|
||||
out.write(header);
|
||||
out.write(("<p>" + _("This seems to be a bad destination:") + " " + ahelperKey + " " + _("i2paddresshelper cannot help you with a destination like that!") + "</p>").getBytes("UTF-8"));
|
||||
@@ -547,7 +545,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
closeSocket(s);
|
||||
return;
|
||||
}
|
||||
ahelperKey = dest.toBase64();
|
||||
ahelperKey = _dest.toBase64();
|
||||
}
|
||||
|
||||
ahelperPresent = true;
|
||||
@@ -560,10 +558,11 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
if((!ahelperNew) && !old.equals(ahelperKey)) {
|
||||
// Conflict: handle when URL reconstruction done
|
||||
ahelperConflict = true;
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
if(_log.shouldLog(Log.WARN)) {
|
||||
_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
|
||||
@@ -573,9 +572,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
if(destB64 != null && !destB64.equals(ahelperKey)) {
|
||||
// Conflict: handle when URL reconstruction done
|
||||
ahelperConflict = true;
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
if(_log.shouldLog(Log.WARN)) {
|
||||
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination +
|
||||
"], trusted key [" + destB64 + "], specified key [" + ahelperKey + "].");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -617,14 +617,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
} // end query processing
|
||||
|
||||
String addressHelper = addressHelpers.get(destination);
|
||||
if (addressHelper != null)
|
||||
if(addressHelper != null) {
|
||||
host = getHostName(addressHelper);
|
||||
}
|
||||
|
||||
// now strip everything but path and query from URI
|
||||
targetRequest = requestURI.toASCIIString();
|
||||
String newURI = requestURI.getRawPath();
|
||||
if (query != null)
|
||||
if(query != null) {
|
||||
newURI += '?' + query;
|
||||
}
|
||||
try {
|
||||
requestURI = new URI(newURI);
|
||||
} catch(URISyntaxException use) {
|
||||
@@ -646,20 +648,24 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
s.close();
|
||||
return;
|
||||
} else if(host.contains(".") || host.startsWith("[")) {
|
||||
if (port >= 0)
|
||||
if(port >= 0) {
|
||||
host = host + ':' + port;
|
||||
}
|
||||
// The request must be forwarded to a WWW proxy
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
if(_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug("Before selecting outproxy for " + host);
|
||||
}
|
||||
currentProxy = selectProxy();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
if(_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug("After selecting outproxy for " + host + ": " + currentProxy);
|
||||
}
|
||||
if(currentProxy == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
if(_log.shouldLog(Log.WARN)) {
|
||||
_log.warn(getPrefix(requestId) + "Host wants to be outproxied, but we dont have any!");
|
||||
}
|
||||
l.log("No HTTP outproxy found for the request.");
|
||||
if(out != null) {
|
||||
out.write(getErrorPage("noproxy", ERR_NO_OUTPROXY));
|
||||
out.write(getErrorPage("noproxy", _ERR_NO_OUTPROXY));
|
||||
writeFooter(out);
|
||||
}
|
||||
s.close();
|
||||
@@ -668,15 +674,17 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
destination = currentProxy;
|
||||
usingWWWProxy = true;
|
||||
targetRequest = requestURI.toASCIIString();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
if(_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug(getPrefix(requestId) + " [" + host + "]: wwwProxy!");
|
||||
}
|
||||
} else {
|
||||
// what is left for here? a hostname with no dots, and != "i2p"
|
||||
// and not a destination ???
|
||||
// Perhaps something in privatehosts.txt ...
|
||||
// Rather than look it up, just bail out.
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
if(_log.shouldLog(Log.WARN)) {
|
||||
_log.warn("NODOTS, NOI2P: " + request);
|
||||
}
|
||||
if(out != null) {
|
||||
out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
|
||||
writeFooter(out);
|
||||
@@ -687,7 +695,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
|
||||
boolean isValid = usingWWWProxy || usingInternalServer || isSupportedAddress(host, protocol);
|
||||
if(!isValid) {
|
||||
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(requestId) + "notValid(" + host + ")");
|
||||
if(_log.shouldLog(Log.INFO)) {
|
||||
_log.info(getPrefix(requestId) + "notValid(" + host + ")");
|
||||
}
|
||||
method = null;
|
||||
destination = null;
|
||||
break;
|
||||
@@ -708,8 +718,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
// Note that we only pass the original Host: line through to the outproxy
|
||||
// But we don't create a Host: line if it wasn't sent to us
|
||||
line = "Host: " + host;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
if(_log.shouldLog(Log.INFO)) {
|
||||
_log.info(getPrefix(requestId) + "Setting host = " + host);
|
||||
}
|
||||
} else if(lowercaseLine.startsWith("user-agent: ")) {
|
||||
// save for deciding whether to offer address book form
|
||||
userAgent = lowercaseLine.substring(12);
|
||||
@@ -746,11 +757,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
// hop-by-hop header, and we definitely want to block Windows NTLM after a far-end 407.
|
||||
// Response to far-end shouldn't happen, as we
|
||||
// strip Proxy-Authenticate from the response in HTTPResponseOutputStream
|
||||
if (lowercaseLine.startsWith("proxy-authorization: basic "))
|
||||
// save for auth check below
|
||||
if(lowercaseLine.startsWith("proxy-authorization: basic ")) // save for auth check below
|
||||
{
|
||||
authorization = line.substring(27); // "proxy-authorization: basic ".length()
|
||||
}
|
||||
line = null;
|
||||
continue;
|
||||
} else if(lowercaseLine.startsWith("icy")) {
|
||||
// icecast/shoutcast, We need to leave the user-agent alone.
|
||||
shout = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -758,8 +773,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
// No more headers, add our own and break out of the loop
|
||||
String ok = getTunnel().getClientOptions().getProperty("i2ptunnel.gzip");
|
||||
boolean gzip = DEFAULT_GZIP;
|
||||
if (ok != null)
|
||||
if(ok != null) {
|
||||
gzip = Boolean.valueOf(ok).booleanValue();
|
||||
}
|
||||
if(gzip && !usingInternalServer) {
|
||||
// according to rfc2616 s14.3, this *should* force identity, even if
|
||||
// an explicit q=0 for gzip doesn't. tested against orion.i2p, and it
|
||||
@@ -767,13 +783,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
newRequest.append("Accept-Encoding: \r\n");
|
||||
newRequest.append("X-Accept-Encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n");
|
||||
}
|
||||
if(!shout) {
|
||||
if(!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_USER_AGENT)).booleanValue()) {
|
||||
// let's not advertise to external sites that we are from I2P
|
||||
if (usingWWWProxy)
|
||||
if(usingWWWProxy) {
|
||||
newRequest.append("User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6\r\n");
|
||||
else
|
||||
} else {
|
||||
newRequest.append("User-Agent: MYOB/6.66 (AN/ON)\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add Proxy-Authentication header for next hop (outproxy)
|
||||
if(usingWWWProxy && Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_AUTH)).booleanValue()) {
|
||||
// specific for this proxy
|
||||
@@ -785,8 +804,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
pw = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_PW);
|
||||
}
|
||||
if(user != null && pw != null) {
|
||||
newRequest.append("Proxy-Authorization: Basic ")
|
||||
.append(Base64.encode((user + ':' + pw).getBytes(), true)) // true = use standard alphabet
|
||||
newRequest.append("Proxy-Authorization: Basic ").append(Base64.encode((user + ':' + pw).getBytes(), true)) // true = use standard alphabet
|
||||
.append("\r\n");
|
||||
}
|
||||
}
|
||||
@@ -797,33 +815,37 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
}
|
||||
} // end header processing
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
if(_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug(getPrefix(requestId) + "NewRequest header: [" + newRequest.toString() + "]");
|
||||
}
|
||||
|
||||
if(method == null || destination == null) {
|
||||
//l.log("No HTTP method found in the request.");
|
||||
if(out != null) {
|
||||
if (protocol != null && "http".equals(protocol.toLowerCase(Locale.US)))
|
||||
if(protocol != null && "http".equals(protocol.toLowerCase(Locale.US))) {
|
||||
out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
|
||||
else
|
||||
} else {
|
||||
out.write(getErrorPage("protocol", ERR_BAD_PROTOCOL));
|
||||
}
|
||||
writeFooter(out);
|
||||
}
|
||||
s.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
if(_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug(getPrefix(requestId) + "Destination: " + destination);
|
||||
}
|
||||
|
||||
// Authorization
|
||||
if(!authorize(s, requestId, authorization)) {
|
||||
if(_log.shouldLog(Log.WARN)) {
|
||||
if (authorization != null)
|
||||
if(authorization != null) {
|
||||
_log.warn(getPrefix(requestId) + "Auth failed, sending 407 again");
|
||||
else
|
||||
} else {
|
||||
_log.warn(getPrefix(requestId) + "Auth required, sending 407");
|
||||
}
|
||||
}
|
||||
out.write(getErrorPage("auth", ERR_AUTH));
|
||||
writeFooter(out);
|
||||
s.close();
|
||||
@@ -855,8 +877,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
if(clientDest == null) {
|
||||
// remove bad entries
|
||||
addressHelpers.remove(destination.toLowerCase(Locale.US));
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
if(_log.shouldLog(Log.WARN)) {
|
||||
_log.warn(getPrefix(requestId) + "Could not find destination for " + addressHelper);
|
||||
}
|
||||
byte[] header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
|
||||
writeErrorMessage(header, out, targetRequest, false, destination, null);
|
||||
s.close();
|
||||
@@ -871,8 +894,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
if(sess != null && !sess.isClosed()) {
|
||||
byte[] hData = Base32.decode(destination.substring(0, 52));
|
||||
if(hData != null) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
if(_log.shouldLog(Log.INFO)) {
|
||||
_log.info("lookup in-session " + destination);
|
||||
}
|
||||
Hash hash = Hash.create(hData);
|
||||
clientDest = sess.lookupDest(hash, 20 * 1000);
|
||||
}
|
||||
@@ -885,22 +909,24 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
|
||||
if(clientDest == null) {
|
||||
//l.log("Could not resolve " + destination + ".");
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
if(_log.shouldLog(Log.WARN)) {
|
||||
_log.warn("Unable to resolve " + destination + " (proxy? " + usingWWWProxy + ", request: " + targetRequest);
|
||||
}
|
||||
byte[] header;
|
||||
String jumpServers = null;
|
||||
if (usingWWWProxy)
|
||||
if(usingWWWProxy) {
|
||||
header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN);
|
||||
else if (ahelperPresent)
|
||||
} else if(ahelperPresent) {
|
||||
header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
|
||||
else if (destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p"))
|
||||
} else if(destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
|
||||
header = getErrorPage("dnf", ERR_DESTINATION_UNKNOWN);
|
||||
else {
|
||||
} else {
|
||||
header = getErrorPage("dnfh", ERR_DESTINATION_UNKNOWN);
|
||||
jumpServers = getTunnel().getClientOptions().getProperty(PROP_JUMP_SERVERS);
|
||||
if (jumpServers == null)
|
||||
if(jumpServers == null) {
|
||||
jumpServers = DEFAULT_JUMP_SERVERS;
|
||||
}
|
||||
}
|
||||
writeErrorMessage(header, out, targetRequest, usingWWWProxy, destination, jumpServers);
|
||||
s.close();
|
||||
return;
|
||||
@@ -923,8 +949,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
// Syndie can't handle a redirect of a POST
|
||||
if(ahelperPresent && !"POST".equals(method)) {
|
||||
String uri = targetRequest;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
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"));
|
||||
@@ -942,20 +969,23 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
|
||||
} catch (SocketException ex) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
if (_log.shouldLog(Log.INFO)) {
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
}
|
||||
//l.log("Error connecting: " + ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
} catch(IOException ex) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
if(_log.shouldLog(Log.INFO)) {
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
}
|
||||
//l.log("Error connecting: " + ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
} catch(I2PException ex) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
if(_log.shouldLog(Log.INFO)) {
|
||||
_log.info("getPrefix(requestId) + Error trying to connect", ex);
|
||||
}
|
||||
//l.log("Error connecting: " + ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
@@ -970,8 +1000,9 @@ 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)
|
||||
if(out == null) {
|
||||
return;
|
||||
}
|
||||
byte[] header = getErrorPage("ahelper-new", ERR_AHELPER_NEW);
|
||||
out.write(header);
|
||||
out.write(("<table><tr><td class=\"mediumtags\" align=\"right\">" + _("Host") +
|
||||
@@ -980,7 +1011,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
String b32 = Base32.encode(SHA256Generator.getInstance().calculateHash(Base64.decode(ahelperKey)).getData());
|
||||
out.write(("<tr><td class=\"mediumtags\" align=\"right\">" + _("Base 32") + "</td>" +
|
||||
"<td><a href=\"http://" + b32 + ".b32.i2p/\">" + b32 + ".b32.i2p</a></td></tr>").getBytes());
|
||||
} catch (Exception e) {}
|
||||
} catch(Exception e) {
|
||||
}
|
||||
out.write(("<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" +
|
||||
@@ -1015,9 +1047,11 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
*/
|
||||
private static class InputReader {
|
||||
InputStream _s;
|
||||
|
||||
public InputReader(InputStream s) {
|
||||
_s = s;
|
||||
}
|
||||
|
||||
String readLine(String method) throws IOException {
|
||||
// Use unbuffered until we can find a BufferedReader that limits line length
|
||||
//if (method == null || "POST".equals(method))
|
||||
@@ -1033,12 +1067,17 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
* Prior to 0.7.12, returned b64 key
|
||||
*/
|
||||
private final String getHostName(String host) {
|
||||
if (host == null) return null;
|
||||
if (host.length() == 60 && host.toLowerCase(Locale.US).endsWith(".b32.i2p"))
|
||||
if(host == null) {
|
||||
return null;
|
||||
}
|
||||
if(host.length() == 60 && host.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
|
||||
return host;
|
||||
Destination dest = _context.namingService().lookup(host);
|
||||
if (dest == null) return "i2p";
|
||||
return Base32.encode(dest.calculateHash().getData()) + ".b32.i2p";
|
||||
}
|
||||
Destination _dest = _context.namingService().lookup(host);
|
||||
if(_dest == null) {
|
||||
return "i2p";
|
||||
}
|
||||
return Base32.encode(_dest.calculateHash().getData()) + ".b32.i2p";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1086,7 +1125,12 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
}
|
||||
return baos.toByteArray();
|
||||
} finally {
|
||||
try { if (fis != null) fis.close(); } catch (IOException foo) {}
|
||||
try {
|
||||
if(fis != null) {
|
||||
fis.close();
|
||||
}
|
||||
} catch(IOException foo) {
|
||||
}
|
||||
}
|
||||
// we won't ever get here
|
||||
}
|
||||
@@ -1103,12 +1147,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
}
|
||||
|
||||
private static class OnTimeout implements Runnable {
|
||||
|
||||
private Socket _socket;
|
||||
private OutputStream _out;
|
||||
private String _target;
|
||||
private boolean _usingProxy;
|
||||
private String _wwwProxy;
|
||||
private long _requestId;
|
||||
|
||||
public OnTimeout(Socket s, OutputStream out, String target, boolean usingProxy, String wwwProxy, long id) {
|
||||
_socket = s;
|
||||
_out = out;
|
||||
@@ -1117,6 +1163,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
_wwwProxy = wwwProxy;
|
||||
_requestId = id;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Timeout occured requesting " + _target);
|
||||
@@ -1125,7 +1172,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
closeSocket(_socket);
|
||||
}
|
||||
}
|
||||
|
||||
public static final String DEFAULT_JUMP_SERVERS =
|
||||
"http://i2host.i2p/cgi-bin/i2hostjump?," +
|
||||
"http://stats.i2p/cgi-bin/jump.cgi?a=," +
|
||||
@@ -1155,21 +1201,26 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
out.write(_("Click a link below to look for an address helper by using a \"jump\" service:").getBytes("UTF-8"));
|
||||
out.write("<br>\n".getBytes());
|
||||
|
||||
if (uri.startsWith("http://"))
|
||||
if(uri.startsWith("http://")) {
|
||||
uri = uri.substring(7);
|
||||
}
|
||||
StringTokenizer tok = new StringTokenizer(jumpServers, ", ");
|
||||
while(tok.hasMoreTokens()) {
|
||||
String jurl = tok.nextToken();
|
||||
if (!jurl.startsWith("http://"))
|
||||
if(!jurl.startsWith("http://")) {
|
||||
continue;
|
||||
}
|
||||
// Skip jump servers we don't know
|
||||
String jumphost = jurl.substring(7); // "http://"
|
||||
jumphost = jumphost.substring(0, jumphost.indexOf('/'));
|
||||
if (!jumphost.endsWith(".i2p"))
|
||||
if(!jumphost.endsWith(".i2p")) {
|
||||
continue;
|
||||
}
|
||||
if(!jumphost.endsWith(".b32.i2p")) {
|
||||
Destination dest = I2PAppContext.getGlobalContext().namingService().lookup(jumphost);
|
||||
if (dest == null) continue;
|
||||
if(dest == null) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
out.write("<br><a href=\"".getBytes());
|
||||
@@ -1196,10 +1247,11 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
if(out != null) {
|
||||
try {
|
||||
byte[] header;
|
||||
if (usingWWWProxy)
|
||||
if(usingWWWProxy) {
|
||||
header = getErrorPage(I2PAppContext.getGlobalContext(), "dnfp", ERR_DESTINATION_UNKNOWN);
|
||||
else
|
||||
} else {
|
||||
header = getErrorPage(I2PAppContext.getGlobalContext(), "dnf", ERR_DESTINATION_UNKNOWN);
|
||||
}
|
||||
writeErrorMessage(header, out, targetRequest, usingWWWProxy, wwwProxy, null);
|
||||
} catch(IOException ioe) {
|
||||
// static
|
||||
@@ -1213,7 +1265,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
|
||||
/** @param host ignored */
|
||||
private static boolean isSupportedAddress(String host, String protocol) {
|
||||
if ((host == null) || (protocol == null)) return false;
|
||||
if((host == null) || (protocol == null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/****
|
||||
* Let's not look up the name _again_
|
||||
@@ -1238,13 +1292,11 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
****/
|
||||
return protocol.toLowerCase(Locale.US).equals("http");
|
||||
}
|
||||
|
||||
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();
|
||||
"Address helpers disabled").getBytes();
|
||||
|
||||
/**
|
||||
* Change various parts of the URI.
|
||||
@@ -1314,17 +1366,19 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
char c = i < query.length() ? query.charAt(i) : '&';
|
||||
if(c == ';' || c == '&') {
|
||||
// end of key or value
|
||||
if (valstart < 0)
|
||||
if(valstart < 0) {
|
||||
key = query.substring(keystart, i);
|
||||
}
|
||||
String decodedKey = LocalHTTPServer.decode(key);
|
||||
if(decodedKey.equals(HELPER_PARAM)) {
|
||||
String newQuery = keystart > 0 ? query.substring(0, keystart - 1) : "";
|
||||
if(i < query.length() - 1) {
|
||||
if (keystart > 0)
|
||||
if(keystart > 0) {
|
||||
newQuery += query.substring(i);
|
||||
else
|
||||
} else {
|
||||
newQuery += query.substring(i + 1);
|
||||
}
|
||||
}
|
||||
String value = valstart >= 0 ? query.substring(valstart, i) : "";
|
||||
String helperValue = LocalHTTPServer.decode(value);
|
||||
return new String[] {newQuery, helperValue};
|
||||
@@ -1339,7 +1393,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/****
|
||||
private static String[] tests = {
|
||||
"", "foo", "foo=bar", "&", "&=&", "===", "&&",
|
||||
@@ -1366,7 +1419,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
}
|
||||
}
|
||||
****/
|
||||
|
||||
/** */
|
||||
private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.web.messages";
|
||||
|
||||
@@ -1384,5 +1436,4 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
protected static String _(String key, Object o, Object o2) {
|
||||
return Translate.getString(key, o, o2, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,3 +1,7 @@
|
||||
2012-03-27 sponge
|
||||
* A hopeful fix to allow SHOUTcast/icecast to work over the http proxy.
|
||||
* A little more code clean up
|
||||
|
||||
2012-03-26 zzz
|
||||
* Code cleanups:
|
||||
- Remove unused imports
|
||||
|
@@ -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 = 22;
|
||||
public final static long BUILD = 23;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
Reference in New Issue
Block a user