* I2CP: Fix the SessionConfig serializer in DataHelper,
so that UTF-8 tunnel names are not corrupted by I2CP and can be displayed on the console * Fix UTF-8 form submission on console and i2ptunnel
This commit is contained in:
@ -1,3 +1,4 @@
|
|||||||
|
<%@page pageEncoding="UTF-8"%>
|
||||||
<%@page contentType="text/html" import="net.i2p.i2ptunnel.web.EditBean" %><%
|
<%@page contentType="text/html" import="net.i2p.i2ptunnel.web.EditBean" %><%
|
||||||
String tun = request.getParameter("tunnel");
|
String tun = request.getParameter("tunnel");
|
||||||
if (tun != null) {
|
if (tun != null) {
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
<%
|
||||||
|
// http://www.crazysquirrel.com/computing/general/form-encoding.jspx
|
||||||
|
if (request.getCharacterEncoding() == null)
|
||||||
|
request.setCharacterEncoding("UTF-8");
|
||||||
|
%>
|
||||||
|
<%@page pageEncoding="UTF-8"%>
|
||||||
<%@page contentType="text/html" import="net.i2p.i2ptunnel.web.IndexBean"%><?xml version="1.0" encoding="UTF-8"?>
|
<%@page contentType="text/html" import="net.i2p.i2ptunnel.web.IndexBean"%><?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
<jsp:useBean class="net.i2p.i2ptunnel.web.IndexBean" id="indexBean" scope="request" />
|
<jsp:useBean class="net.i2p.i2ptunnel.web.IndexBean" id="indexBean" scope="request" />
|
||||||
@ -6,13 +12,12 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>I2P Tunnel Manager - List</title>
|
<title>I2P Tunnel Manager - List</title>
|
||||||
|
|
||||||
<meta htt
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
p-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
||||||
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
|
||||||
|
<link href="/themes/console/images/favicon.ico" type="image/x-icon" rel="shortcut icon" />
|
||||||
|
|
||||||
<% if (indexBean.allowCSS()) {
|
<% if (indexBean.allowCSS()) {
|
||||||
%><link href="/themes/console/images/favicon.ico" type="image/x-icon" rel="shortcut icon" />
|
%><link href="<%=indexBean.getTheme()%>default.css" rel="stylesheet" type="text/css" />
|
||||||
<link href="<%=indexBean.getTheme()%>default.css" rel="stylesheet" type="text/css" />
|
|
||||||
<link href="<%=indexBean.getTheme()%>i2ptunnel.css" rel="stylesheet" type="text/css" />
|
<link href="<%=indexBean.getTheme()%>i2ptunnel.css" rel="stylesheet" type="text/css" />
|
||||||
<% }
|
<% }
|
||||||
%>
|
%>
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
* This is included almost 30 times, so keep whitespace etc. to a minimum.
|
* This is included almost 30 times, so keep whitespace etc. to a minimum.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// http://www.crazysquirrel.com/computing/general/form-encoding.jspx
|
||||||
|
if (request.getCharacterEncoding() == null)
|
||||||
|
request.setCharacterEncoding("UTF-8");
|
||||||
|
|
||||||
response.setHeader("Pragma", "no-cache");
|
response.setHeader("Pragma", "no-cache");
|
||||||
response.setHeader("Cache-Control","no-cache");
|
response.setHeader("Cache-Control","no-cache");
|
||||||
response.setDateHeader("Expires", 0);
|
response.setDateHeader("Expires", 0);
|
||||||
|
@ -105,6 +105,19 @@ public class DataHelper {
|
|||||||
*/
|
*/
|
||||||
public static void writeProperties(OutputStream rawStream, Properties props)
|
public static void writeProperties(OutputStream rawStream, Properties props)
|
||||||
throws DataFormatException, IOException {
|
throws DataFormatException, IOException {
|
||||||
|
writeProperties(rawStream, props, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* jrandom disabled UTF-8 in mid-2004, for performance reasons,
|
||||||
|
* i.e. slow foo.getBytes("UTF-8")
|
||||||
|
* Re-enable it so we can pass UTF-8 tunnel names through the I2CP SessionConfig.
|
||||||
|
*
|
||||||
|
* Use utf8 = false for RouterAddress (fast, non UTF-8)
|
||||||
|
* Use utf8 = true for SessionConfig (slow, UTF-8)
|
||||||
|
*/
|
||||||
|
public static void writeProperties(OutputStream rawStream, Properties props, boolean utf8)
|
||||||
|
throws DataFormatException, IOException {
|
||||||
if (props != null) {
|
if (props != null) {
|
||||||
OrderedProperties p = new OrderedProperties();
|
OrderedProperties p = new OrderedProperties();
|
||||||
p.putAll(props);
|
p.putAll(props);
|
||||||
@ -112,12 +125,15 @@ public class DataHelper {
|
|||||||
for (Iterator iter = p.keySet().iterator(); iter.hasNext();) {
|
for (Iterator iter = p.keySet().iterator(); iter.hasNext();) {
|
||||||
String key = (String) iter.next();
|
String key = (String) iter.next();
|
||||||
String val = p.getProperty(key);
|
String val = p.getProperty(key);
|
||||||
// now make sure they're in UTF-8
|
if (utf8)
|
||||||
//key = new String(key.getBytes(), "UTF-8");
|
writeStringUTF8(baos, key);
|
||||||
//val = new String(val.getBytes(), "UTF-8");
|
else
|
||||||
writeString(baos, key);
|
writeString(baos, key);
|
||||||
baos.write(_equalBytes);
|
baos.write(_equalBytes);
|
||||||
writeString(baos, val);
|
if (utf8)
|
||||||
|
writeStringUTF8(baos, val);
|
||||||
|
else
|
||||||
|
writeString(baos, val);
|
||||||
baos.write(_semicolonBytes);
|
baos.write(_semicolonBytes);
|
||||||
}
|
}
|
||||||
baos.close();
|
baos.close();
|
||||||
@ -486,6 +502,7 @@ public class DataHelper {
|
|||||||
/** Read in a string from the stream as specified by the I2P data structure spec.
|
/** Read in a string from the stream as specified by the I2P data structure spec.
|
||||||
* A string is 1 or more bytes where the first byte is the number of bytes (not characters!)
|
* A string is 1 or more bytes where the first byte is the number of bytes (not characters!)
|
||||||
* in the string and the remaining 0-255 bytes are the non-null terminated UTF-8 encoded character array.
|
* in the string and the remaining 0-255 bytes are the non-null terminated UTF-8 encoded character array.
|
||||||
|
*
|
||||||
* @param in stream to read from
|
* @param in stream to read from
|
||||||
* @throws DataFormatException if the stream doesn't contain a validly formatted string
|
* @throws DataFormatException if the stream doesn't contain a validly formatted string
|
||||||
* @throws IOException if there is an IO error reading the string
|
* @throws IOException if there is an IO error reading the string
|
||||||
@ -496,12 +513,16 @@ public class DataHelper {
|
|||||||
byte raw[] = new byte[size];
|
byte raw[] = new byte[size];
|
||||||
int read = read(in, raw);
|
int read = read(in, raw);
|
||||||
if (read != size) throw new DataFormatException("Not enough bytes to read the string");
|
if (read != size) throw new DataFormatException("Not enough bytes to read the string");
|
||||||
return new String(raw);
|
// the following constructor throws an UnsupportedEncodingException which is an IOException,
|
||||||
|
// but that's only if UTF-8 is not supported. Other encoding errors are not thrown.
|
||||||
|
return new String(raw, "UTF-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Write out a string to the stream as specified by the I2P data structure spec. Note that the max
|
/** Write out a string to the stream as specified by the I2P data structure spec. Note that the max
|
||||||
* size for a string allowed by the spec is 255 bytes.
|
* size for a string allowed by the spec is 255 bytes.
|
||||||
*
|
*
|
||||||
|
* WARNING - this method destroys the encoding
|
||||||
|
*
|
||||||
* @param out stream to write string
|
* @param out stream to write string
|
||||||
* @param string string to write out: null strings are perfectly valid, but strings of excess length will
|
* @param string string to write out: null strings are perfectly valid, but strings of excess length will
|
||||||
* cause a DataFormatException to be thrown
|
* cause a DataFormatException to be thrown
|
||||||
@ -523,6 +544,34 @@ public class DataHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Write out a string to the stream as specified by the I2P data structure spec. Note that the max
|
||||||
|
* size for a string allowed by the spec is 255 bytes.
|
||||||
|
*
|
||||||
|
* This method correctly uses UTF-8
|
||||||
|
*
|
||||||
|
* @param out stream to write string
|
||||||
|
* @param string UTF-8 string to write out: null strings are perfectly valid, but strings of excess length will
|
||||||
|
* cause a DataFormatException to be thrown
|
||||||
|
* @throws DataFormatException if the string is not valid
|
||||||
|
* @throws IOException if there is an IO error writing the string
|
||||||
|
*/
|
||||||
|
private static void writeStringUTF8(OutputStream out, String string)
|
||||||
|
throws DataFormatException, IOException {
|
||||||
|
if (string == null) {
|
||||||
|
writeLong(out, 1, 0);
|
||||||
|
} else {
|
||||||
|
// the following method throws an UnsupportedEncodingException which is an IOException,
|
||||||
|
// but that's only if UTF-8 is not supported. Other encoding errors are not thrown.
|
||||||
|
byte[] raw = string.getBytes("UTF-8");
|
||||||
|
int len = raw.length;
|
||||||
|
if (len > 255)
|
||||||
|
throw new DataFormatException("The I2P data spec limits strings to 255 bytes or less, but this is "
|
||||||
|
+ string.length() + " [" + string + "]");
|
||||||
|
writeLong(out, 1, len);
|
||||||
|
out.write(raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Read in a boolean as specified by the I2P data structure spec.
|
/** Read in a boolean as specified by the I2P data structure spec.
|
||||||
* A boolean is 1 byte that is either 0 (false), 1 (true), or 2 (null)
|
* A boolean is 1 byte that is either 0 (false), 1 (true), or 2 (null)
|
||||||
* @param in stream to read from
|
* @param in stream to read from
|
||||||
|
@ -173,7 +173,7 @@ public class SessionConfig extends DataStructureImpl {
|
|||||||
_log.debug("PubKey size for destination: " + _destination.getPublicKey().getData().length);
|
_log.debug("PubKey size for destination: " + _destination.getPublicKey().getData().length);
|
||||||
_log.debug("SigningKey size for destination: " + _destination.getSigningPublicKey().getData().length);
|
_log.debug("SigningKey size for destination: " + _destination.getSigningPublicKey().getData().length);
|
||||||
_destination.writeBytes(out);
|
_destination.writeBytes(out);
|
||||||
DataHelper.writeProperties(out, _options);
|
DataHelper.writeProperties(out, _options, true); // UTF-8
|
||||||
DataHelper.writeDate(out, _creationDate);
|
DataHelper.writeDate(out, _creationDate);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
_log.error("IOError signing", ioe);
|
_log.error("IOError signing", ioe);
|
||||||
@ -198,7 +198,7 @@ public class SessionConfig extends DataStructureImpl {
|
|||||||
if ((_destination == null) || (_options == null) || (_signature == null) || (_creationDate == null))
|
if ((_destination == null) || (_options == null) || (_signature == null) || (_creationDate == null))
|
||||||
throw new DataFormatException("Not enough data to create the session config");
|
throw new DataFormatException("Not enough data to create the session config");
|
||||||
_destination.writeBytes(out);
|
_destination.writeBytes(out);
|
||||||
DataHelper.writeProperties(out, _options);
|
DataHelper.writeProperties(out, _options, true); // UTF-8
|
||||||
DataHelper.writeDate(out, _creationDate);
|
DataHelper.writeDate(out, _creationDate);
|
||||||
_signature.writeBytes(out);
|
_signature.writeBytes(out);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user