* susidns:

- Enforce basic rules on host name additions
- Split up form
- IDN toASCII improvements
- Log message improvements
This commit is contained in:
zzz
2011-03-29 22:46:59 +00:00
parent 172541a368
commit 9e37a5a4af
5 changed files with 148 additions and 56 deletions

View File

@@ -72,13 +72,23 @@ public class AddressBean
/** /**
* The Unicode name, translated from Punycode * The Unicode name, translated from Punycode
* @return the original string on error
* @since 0.8.6 * @since 0.8.6
*/ */
public String getDisplayName() public String getDisplayName()
{ {
return toUnicode(name);
}
/**
* The Unicode name, translated from Punycode
* @return the original string on error
* @since 0.8.6
*/
public static String toUnicode(String host) {
if (haveIDN) if (haveIDN)
return IDN.toUnicode(name); return IDN.toUnicode(host);
return name; return host;
} }
/** /**
@@ -90,6 +100,65 @@ public class AddressBean
return haveIDN && !IDN.toUnicode(name).equals(name); return haveIDN && !IDN.toUnicode(name).equals(name);
} }
private static final char DOT = '.';
private static final char DOT2 = 0x3002;
private static final char DOT3 = 0xFF0E;
private static final char DOT4 = 0xFF61;
/**
* Ref: java.net.IDN and RFC 3940
* @param name will be converted to lower case
* @return name converted to lower case and punycoded if necessary
* @throws IAE on various errors or if IDN is needed but not available
* @since 0.8.6
*/
static String toASCII(String host) throws IllegalArgumentException {
host = host.toLowerCase();
boolean needsIDN = false;
// Here we do easy checks and throw translated exceptions.
// We do checks on the whole host name, not on each "label", so
// we allow '.', and some untranslated errors will be thrown by IDN.toASCII()
for (int i = 0; i < host.length(); i++) {
char c = host.charAt(i);
if (c <= 0x2c ||
c == 0x2f ||
c >= 0x3a && c <= 0x40 ||
c >= 0x5b && c <= 0x60 ||
c >= 0x7b && c <= 0x7f) {
String bad = "\"" + c + "\" (0x" + Integer.toHexString(c) + ')';
throw new IllegalArgumentException(_("Host name \"{0}\" contains illegal character {1}",
host, bad));
}
if (c == DOT2)
host = host.replace(DOT2, DOT);
else if (c == DOT3)
host = host.replace(DOT3, DOT);
else if (c == DOT4)
host = host.replace(DOT4, DOT);
else if (c > 0x7f)
needsIDN = true;
}
if (host.startsWith("-"))
throw new IllegalArgumentException(_("Host name cannot start with \"{0}\"", "-"));
if (host.startsWith("."))
throw new IllegalArgumentException(_("Host name cannot start with \"{0}\"", "."));
if (host.endsWith("-"))
throw new IllegalArgumentException(_("Host name cannot end with \"{0}\"", "-"));
if (host.endsWith("."))
throw new IllegalArgumentException(_("Host name cannot end with \"{0}\"", "."));
if (needsIDN) {
if (host.startsWith("xn--"))
throw new IllegalArgumentException(_("Host name cannot start with \"{0}\"", "xn--"));
if (host.contains(".xn--"))
throw new IllegalArgumentException(_("Host name cannot contain \"{0}\"", ".xn--"));
if (haveIDN)
return IDN.toASCII(host, IDN.ALLOW_UNASSIGNED);
throw new IllegalArgumentException(_("Host name \"{0}\" requires conversion to ASCII but the conversion library is unavailable in this JVM", host));
}
return host;
}
/** @since 0.8.6 */ /** @since 0.8.6 */
public String getB32() public String getB32()
{ {
@@ -183,4 +252,9 @@ public class AddressBean
private static String _(String s, Object o) { private static String _(String s, Object o) {
return Messages.getString(s, o); return Messages.getString(s, o);
} }
/** translate */
private static String _(String s, Object o, Object o2) {
return Messages.getString(s, o, o2);
}
} }

View File

@@ -58,7 +58,7 @@ public class AddressbookBean
return search; return search;
} }
public void setSearch(String search) { public void setSearch(String search) {
this.search = search; this.search = DataHelper.stripHTML(search).trim(); // XSS;
} }
public boolean isHasFilter() public boolean isHasFilter()
{ {
@@ -331,6 +331,8 @@ public class AddressbookBean
} else { } else {
message = _("No entries selected to delete."); message = _("No entries selected to delete.");
} }
if (action.equals(_("Delete Entry")))
search = null;
} }
if( changed ) { if( changed ) {
try { try {
@@ -390,7 +392,7 @@ public class AddressbookBean
filter = null; filter = null;
search = null; search = null;
} }
this.filter = filter; this.filter = DataHelper.stripHTML(filter); // XSS
} }
public String getDestination() { public String getDestination() {
return destination; return destination;
@@ -405,7 +407,7 @@ public class AddressbookBean
deletionMarks.clear(); deletionMarks.clear();
} }
public void setMarkedForDeletion( String name ) { public void setMarkedForDeletion( String name ) {
deletionMarks.addLast( name ); deletionMarks.addLast( DataHelper.stripHTML(name) ); // XSS
} }
public void setHostname(String hostname) { public void setHostname(String hostname) {
this.hostname = DataHelper.stripHTML(hostname).trim(); // XSS this.hostname = DataHelper.stripHTML(hostname).trim(); // XSS

View File

@@ -221,20 +221,20 @@ public class NamingServiceBean extends AddressbookBean
boolean changed = false; boolean changed = false;
if (action.equals(_("Add")) || action.equals(_("Replace"))) { if (action.equals(_("Add")) || action.equals(_("Replace"))) {
if(hostname != null && destination != null) { if(hostname != null && destination != null) {
try {
// throws IAE with translated message
String host = AddressBean.toASCII(hostname);
String displayHost = host.equals(hostname) ? hostname :
hostname + " (" + host + ')';
Properties outProperties= new Properties(); Properties outProperties= new Properties();
Destination oldDest = getNamingService().lookup(hostname, nsOptions, outProperties); Destination oldDest = getNamingService().lookup(host, nsOptions, outProperties);
if (oldDest != null && destination.equals(oldDest.toBase64())) { if (oldDest != null && destination.equals(oldDest.toBase64())) {
message = _("Host name {0} is already in addressbook, unchanged.", hostname); message = _("Host name {0} is already in addressbook, unchanged.", displayHost);
} else if (oldDest != null && !action.equals(_("Replace"))) { } else if (oldDest != null && !action.equals(_("Replace"))) {
message = _("Host name {0} is already in addressbook with a different destination. Click \"Replace\" to overwrite.", hostname); message = _("Host name {0} is already in addressbook with a different destination. Click \"Replace\" to overwrite.", displayHost);
} else { } else {
try { try {
String host = hostname;
if (AddressBean.haveIDN) {
try {
host = IDN.toASCII(hostname, IDN.ALLOW_UNASSIGNED);
} catch (IllegalArgumentException iae) {}
}
Destination dest = new Destination(destination); Destination dest = new Destination(destination);
if (oldDest != null) { if (oldDest != null) {
nsOptions.putAll(outProperties); nsOptions.putAll(outProperties);
@@ -245,19 +245,24 @@ public class NamingServiceBean extends AddressbookBean
if (success) { if (success) {
changed = true; changed = true;
if (oldDest == null) if (oldDest == null)
message = _("Destination added for {0}.", hostname); message = _("Destination added for {0}.", displayHost);
else else
message = _("Destination changed for {0}.", hostname); message = _("Destination changed for {0}.", displayHost);
// clear form // clear form
hostname = null; hostname = null;
destination = null; destination = null;
} else { } else {
message = _("Failed to add Destination for {0} to naming service {1}", hostname, getNamingService()) + "<br>"; message = _("Failed to add Destination for {0} to naming service {1}", displayHost, getNamingService()) + "<br>";
} }
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
message = _("Invalid Base 64 destination."); message = _("Invalid Base 64 destination.");
} }
} }
} catch (IllegalArgumentException iae) {
message = iae.getMessage();
if (message == null)
message = _("Invalid host name \"{0}\".", hostname);
}
} else { } else {
message = _("Please enter a host name and destination"); message = _("Please enter a host name and destination");
} }
@@ -268,15 +273,18 @@ public class NamingServiceBean extends AddressbookBean
int deleted = 0; int deleted = 0;
for (String n : deletionMarks) { for (String n : deletionMarks) {
boolean success = getNamingService().remove(n, nsOptions); boolean success = getNamingService().remove(n, nsOptions);
String uni = AddressBean.toUnicode(n);
String displayHost = uni.equals(n) ? n : uni + " (" + n + ')';
if (!success) { if (!success) {
message += _("Failed to delete Destination for {0} from naming service {1}", name, getNamingService()) + "<br>"; message += _("Failed to delete Destination for {0} from naming service {1}", displayHost, getNamingService()) + "<br>";
} else if (deleted++ == 0) { } else if (deleted++ == 0) {
changed = true; changed = true;
name = n; name = displayHost;
} }
} }
if( changed ) { if( changed ) {
if (deleted == 1) if (deleted == 1)
// parameter is a host name
message += _("Destination {0} deleted.", name); message += _("Destination {0} deleted.", name);
else else
// parameter will always be >= 2 // parameter will always be >= 2
@@ -284,6 +292,9 @@ public class NamingServiceBean extends AddressbookBean
} else { } else {
message = _("No entries selected to delete."); message = _("No entries selected to delete.");
} }
// clear search when deleting
if (action.equals(_("Delete Entry")))
search = null;
} }
if( changed ) { if( changed ) {
message += "<br>" + _("Addressbook saved."); message += "<br>" + _("Addressbook saved.");

View File

@@ -110,29 +110,29 @@ ${book.loadBookMessages}
</c:if> </c:if>
</div> </div>
<div id="search">
<form method="POST" action="addressbook.jsp"> <form method="POST" action="addressbook.jsp">
<input type="hidden" name="begin" value="0"> <input type="hidden" name="begin" value="0">
<input type="hidden" name="end" value="99"> <input type="hidden" name="end" value="99">
<div id="search">
<table><tr> <table><tr>
<td class="search"><%=intl._("Search")%>: <input type="text" name="search" value="${book.search}" size="20" ></td> <td class="search"><%=intl._("Search")%>: <input type="text" name="search" value="${book.search}" size="20" ></td>
<td class="search"><input type="submit" name="submitsearch" value="<%=intl._("Search")%>" ></td> <td class="search"><input type="submit" name="submitsearch" value="<%=intl._("Search")%>" ></td>
</tr> </tr>
</table> </table>
</div> </form></div>
</form>
</c:if> </c:if>
<%
// have to only do this once per page
String susiNonce = book.getSerial();
%>
<c:if test="${book.notEmpty}">
<form method="POST" action="addressbook.jsp"> <form method="POST" action="addressbook.jsp">
<input type="hidden" name="serial" value="${book.serial}"> <input type="hidden" name="serial" value="<%=susiNonce%>">
<input type="hidden" name="begin" value="0"> <input type="hidden" name="begin" value="0">
<input type="hidden" name="end" value="99"> <input type="hidden" name="end" value="99">
<c:if test="${book.notEmpty}">
<div id="book">
<jsp:setProperty name="book" property="trClass" value="0" /> <jsp:setProperty name="book" property="trClass" value="0" />
<div id="book">
<table class="book" cellspacing="0" cellpadding="5"> <table class="book" cellspacing="0" cellpadding="5">
<tr class="head"> <tr class="head">
@@ -150,11 +150,11 @@ ${book.loadBookMessages}
<c:if test="${book.master || book.router || book.published || book.private}"> <c:if test="${book.master || book.router || book.published || book.private}">
<td class="checkbox"><input type="checkbox" name="checked" value="${addr.name}" title="<%=intl._("Mark for deletion")%>"></td> <td class="checkbox"><input type="checkbox" name="checked" value="${addr.name}" title="<%=intl._("Mark for deletion")%>"></td>
</c:if> </c:if>
<td class="names"><a href="http://${addr.name}/">${addr.displayName}</a> <td class="names"><a href="${addr.uri}">${addr.displayName}</a>
</td><td class="names"> </td><td class="names">
<span class="addrhlpr"><a href="http://${addr.b32}/" title="<%=intl._("Base 32 address")%>">b32</a></span> <span class="addrhlpr"><a href="http://${addr.b32}/" title="<%=intl._("Base 32 address")%>">b32</a></span>
</td><td class="names"> </td><td class="names">
<span class="addrhlpr"><a href="details.jsp?h=${addr.name}" title="<%=intl._("More information on this entry")%>"><%=intl._("details")%></a></span> <span class="addrhlpr"><a href="details.jsp?h=${addr.query}" title="<%=intl._("More information on this entry")%>"><%=intl._("details")%></a></span>
</td> </td>
<td class="destinations"><textarea rows="1" style="height: 3em;" cols="40" wrap="off" readonly="readonly" name="dest_${addr.name}" >${addr.destination}</textarea></td> <td class="destinations"><textarea rows="1" style="height: 3em;" cols="40" wrap="off" readonly="readonly" name="dest_${addr.name}" >${addr.destination}</textarea></td>
</tr> </tr>
@@ -168,7 +168,7 @@ ${book.loadBookMessages}
<input type="reset" value="<%=intl._("Cancel")%>" > <input type="reset" value="<%=intl._("Cancel")%>" >
<input type="submit" name="action" value="<%=intl._("Delete Selected")%>" > <input type="submit" name="action" value="<%=intl._("Delete Selected")%>" >
</p> </p>
</div> </div></form>
</c:if> </c:if>
</c:if> </c:if>
@@ -179,6 +179,10 @@ ${book.loadBookMessages}
</div> </div>
</c:if> </c:if>
<form method="POST" action="addressbook.jsp">
<input type="hidden" name="serial" value="<%=susiNonce%>">
<input type="hidden" name="begin" value="0">
<input type="hidden" name="end" value="99">
<div id="add"> <div id="add">
<h3><%=intl._("Add new destination")%>:</h3> <h3><%=intl._("Add new destination")%>:</h3>
<table><tr><td> <table><tr><td>
@@ -186,14 +190,13 @@ ${book.loadBookMessages}
</td></tr><tr><td> </td></tr><tr><td>
<b><%=intl._("Destination")%></b></td><td><textarea name="destination" rows="1" style="height: 3em;" cols="70" wrap="off" spellcheck="false">${book.destination}</textarea> <b><%=intl._("Destination")%></b></td><td><textarea name="destination" rows="1" style="height: 3em;" cols="70" wrap="off" spellcheck="false">${book.destination}</textarea>
</td></tr></table> </td></tr></table>
<p> <p class="buttons">
<input type="reset" value="<%=intl._("Cancel")%>" > <input type="reset" value="<%=intl._("Cancel")%>" >
<input type="submit" name="action" value="<%=intl._("Replace")%>" > <input type="submit" name="action" value="<%=intl._("Replace")%>" >
<input type="submit" name="action" value="<%=intl._("Add")%>" > <input type="submit" name="action" value="<%=intl._("Add")%>" >
</p> </p>
</div> </div></form>
</form>
<hr> <hr>
<div id="footer"> <div id="footer">
<p class="footer">susidns v${version.version} &copy; <a href="${version.url}">susi</a> 2005</p> <p class="footer">susidns v${version.version} &copy; <a href="${version.url}">susi</a> 2005</p>

View File

@@ -126,11 +126,13 @@
</div> </div>
<div id="buttons"> <div id="buttons">
<form method="POST" action="addressbook.jsp"> <form method="POST" action="addressbook.jsp">
<p class="buttons">
<input type="hidden" name="serial" value="${book.serial}"> <input type="hidden" name="serial" value="${book.serial}">
<input type="hidden" name="begin" value="0"> <input type="hidden" name="begin" value="0">
<input type="hidden" name="end" value="99"> <input type="hidden" name="end" value="99">
<input type="hidden" name="checked" value="<%=detail%>"> <input type="hidden" name="checked" value="<%=detail%>">
<input type="submit" name="action" value="<%=intl._("Delete Entry")%>" > <input type="submit" name="action" value="<%=intl._("Delete Entry")%>" >
</p>
</form> </form>
</div> </div>
<% <%