" +
@@ -1450,7 +1454,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
// FIXME if there is a query remaining it is lost
"\n" +
@@ -1459,7 +1463,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"\n" +
"\n" +
- "
" + _t("Save {0} to router address book and continue to website", destination) + "
\n
" +
+ "
" + _t("Save {0} to router address book and continue to website", idn) + "
\n
" +
_t("This address will be saved to your Router address book where your subscription-based addresses are stored."));
if(_context.namingService().getName().equals("BlockfileNamingService")) {
out.write(" " + _t("If you want to keep track of sites you have added manually, add to your Local or Private address book instead."));
@@ -1472,12 +1476,12 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(_context.namingService().getName().equals("BlockfileNamingService")) {
// only blockfile supports multiple books
- out.write("
" + _t("Save {0} to local address book and continue to website", destination) + "
\n
" +
+ out.write("
" + _t("Save {0} to local address book and continue to website", idn) + "
\n
" +
_t("This address will be saved to your Local address book. Select this option for addresses you wish to keep separate from the main router address book, but don't mind publishing.") +
"
\n\n");
- out.write("
" + _t("Save {0} to private address book and continue to website", destination) + "
\n
" +
+ out.write("
" + _t("Save {0} to private address book and continue to website", idn) + "
\n
" +
_t("This address will be saved to your Private address book, ensuring it is never published.") +
"
\n\n");
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientBase.java
index 54c776206..814145b62 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientBase.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientBase.java
@@ -15,6 +15,7 @@ import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
+import java.net.IDN;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URI;
@@ -144,6 +145,19 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
// very simple, remember last-failed only
private String _lastFailedSSLProxy;
+ /** available as of Java 6 and Android API 9 */
+ private static final boolean _haveIDN;
+ static {
+ boolean h;
+ try {
+ Class.forName("java.net.IDN", false, ClassLoader.getSystemClassLoader());
+ h = true;
+ } catch (ClassNotFoundException cnfe) {
+ h = false;
+ }
+ _haveIDN = h;
+ }
+
protected String getPrefix(long requestId) {
return "HTTPClient[" + _clientId + '/' + requestId + "]: ";
}
@@ -929,7 +943,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
out.write(uri);
out.write("\">");
// Long URLs are handled in CSS
- out.write(uri);
+ out.write(decodeIDNURI(uri));
out.write("");
if (usingWWWProxy) {
out.write("
");
@@ -996,6 +1010,45 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
writeFooter(out);
}
+ /**
+ * Decode the host part of a URI for display.
+ * Returns original string on any error.
+ *
+ * @since 0.9.50
+ */
+ private static String decodeIDNURI(String uri) {
+ if (!_haveIDN)
+ return uri;
+ if (!uri.contains("xn--"))
+ return uri;
+ try {
+ URI u = new URI(uri);
+ String h = u.getHost();
+ String hu = IDN.toUnicode(h);
+ if (hu == null || h.equals(hu))
+ return uri;
+ int idx = uri.indexOf(h);
+ if (idx < 0)
+ return uri;
+ return uri.substring(0, idx) + hu + uri.substring(idx + h.length(), uri.length());
+ } catch(URISyntaxException use) {}
+ return uri;
+ }
+
+ /**
+ * Decode a hostname for display.
+ * Returns original string on any error.
+ *
+ * @since 0.9.50
+ */
+ public static String decodeIDNHost(String host) {
+ if (!_haveIDN)
+ return host;
+ if (!host.contains("xn--"))
+ return host;
+ return IDN.toUnicode(host);
+ }
+
/**
* Flushes.
*
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java
index 5368b1538..2ab1059a9 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java
@@ -365,6 +365,7 @@ public abstract class LocalHTTPServer {
PortMapper pm = I2PAppContext.getGlobalContext().portMapper();
String conURL = pm.getConsoleURL();
+ String idn = I2PTunnelHTTPClientBase.decodeIDNHost(host);
out.write(("HTTP/1.1 200 OK\r\n"+
"Content-Type: text/html; charset=UTF-8\r\n"+
"Referrer-Policy: no-referrer\r\n"+
@@ -372,7 +373,7 @@ public abstract class LocalHTTPServer {
"Proxy-Connection: close\r\n"+
"\r\n"+
""+
- "" + _t("Redirecting to {0}", host) + "\n" +
+ "" + _t("Redirecting to {0}", idn) + "\n" +
"\n" +
"\n" +
"\n" +
@@ -386,8 +387,8 @@ public abstract class LocalHTTPServer {
"
\n" +
"
" +
(success ?
- _t("Saved {0} to the {1} address book, redirecting now.", host, tbook) :
- _t("Failed to save {0} to the {1} address book, redirecting now.", host, tbook)) +
+ _t("Saved {0} to the {1} address book, redirecting now.", idn, tbook) :
+ _t("Failed to save {0} to the {1} address book, redirecting now.", idn, tbook)) +
"