forked from I2P_Developers/i2p.i2p
* Naming services, addressbook, susidns:
- Fix search capability - Fix result count and view within results - Fix published address book - Fix ngettext - Cache size - Fix 0-9 filter - Addressbook updates via API, except for published
This commit is contained in:
@@ -79,6 +79,7 @@ public class AddressbookBean
|
||||
{
|
||||
return addressbook != null && !addressbook.isEmpty();
|
||||
}
|
||||
|
||||
public AddressbookBean()
|
||||
{
|
||||
properties = new Properties();
|
||||
@@ -86,9 +87,11 @@ public class AddressbookBean
|
||||
beginIndex = 0;
|
||||
endIndex = DISPLAY_SIZE - 1;
|
||||
}
|
||||
|
||||
private long configLastLoaded = 0;
|
||||
private static final String PRIVATE_BOOK = "private_addressbook";
|
||||
private static final String DEFAULT_PRIVATE_BOOK = "../privatehosts.txt";
|
||||
|
||||
protected void loadConfig()
|
||||
{
|
||||
long currentTime = System.currentTimeMillis();
|
||||
@@ -113,6 +116,7 @@ public class AddressbookBean
|
||||
try { fis.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
public String getFileName()
|
||||
{
|
||||
loadConfig();
|
||||
@@ -144,7 +148,7 @@ public class AddressbookBean
|
||||
book.compareToIgnoreCase( "router" ) != 0 &&
|
||||
book.compareToIgnoreCase( "private" ) != 0 &&
|
||||
book.compareToIgnoreCase( "published" ) != 0 ))
|
||||
book = "master";
|
||||
book = "router";
|
||||
|
||||
return book;
|
||||
}
|
||||
@@ -215,40 +219,49 @@ public class AddressbookBean
|
||||
* addressbook.jsp catches the case where the whole book is empty.
|
||||
*/
|
||||
protected String generateLoadMessage() {
|
||||
String message = "";
|
||||
String message;
|
||||
String filterArg = "";
|
||||
if( search != null && search.length() > 0 ) {
|
||||
message = _("Search") + ' ';
|
||||
}
|
||||
int resultCount = resultSize();
|
||||
if( filter != null && filter.length() > 0 ) {
|
||||
if( search != null && search.length() > 0 )
|
||||
message = _("Search within filtered list") + ' ';
|
||||
message = ngettext("One result for search within filtered list.",
|
||||
"{0} results for search within filtered list.",
|
||||
resultCount);
|
||||
else
|
||||
message = _("Filtered list") + ' ';
|
||||
message = ngettext("Filtered list contains 1 entry.",
|
||||
"Fltered list contains {0} entries.",
|
||||
resultCount);
|
||||
filterArg = "&filter=" + filter;
|
||||
}
|
||||
if (entries.length == 0) {
|
||||
message += "- " + _("no matches") + '.';
|
||||
} else if (getBeginInt() == 0 && getEndInt() == entries.length - 1) {
|
||||
if (message.length() == 0)
|
||||
message = _("Addressbook") + ' ';
|
||||
if (entries.length <= 0)
|
||||
message += _("contains no entries");
|
||||
} else if( search != null && search.length() > 0 ) {
|
||||
message = ngettext("One result for search.",
|
||||
"{0} results for search.",
|
||||
resultCount);
|
||||
} else {
|
||||
if (resultCount <= 0)
|
||||
// covered in jsp
|
||||
//message = _("This addressbook is empty.");
|
||||
message = "";
|
||||
else
|
||||
message += _(entries.length, "contains 1 entry", "contains {0} entries");
|
||||
message += '.';
|
||||
message = ngettext("Addressbook contains 1 entry.",
|
||||
"Addressbook contains {0} entries.",
|
||||
resultCount);
|
||||
}
|
||||
if (resultCount <= 0) {
|
||||
// nothing to display
|
||||
} else if (getBeginInt() == 0 && getEndInt() == resultCount - 1) {
|
||||
// nothing to display
|
||||
} else {
|
||||
if (getBeginInt() > 0) {
|
||||
int newBegin = Math.max(0, getBeginInt() - DISPLAY_SIZE);
|
||||
int newEnd = Math.max(0, getBeginInt() - 1);
|
||||
message += "<a href=\"addressbook.jsp?book=" + getBook() + filterArg +
|
||||
message += " <a href=\"addressbook.jsp?book=" + getBook() + filterArg +
|
||||
"&begin=" + newBegin + "&end=" + newEnd + "\">" + newBegin +
|
||||
'-' + newEnd + "</a> | ";
|
||||
}
|
||||
message += _("Showing {0} of {1}", "" + getBegin() + '-' + getEnd(), entries.length);
|
||||
if (getEndInt() < entries.length - 1) {
|
||||
int newBegin = Math.min(entries.length - 1, getEndInt() + 1);
|
||||
int newEnd = Math.min(entries.length, getEndInt() + DISPLAY_SIZE);
|
||||
message += ' ' + _("Showing {0} of {1}", "" + getBegin() + '-' + getEnd(), Integer.valueOf(resultCount));
|
||||
if (getEndInt() < resultCount - 1) {
|
||||
int newBegin = Math.min(resultCount - 1, getEndInt() + 1);
|
||||
int newEnd = Math.min(resultCount, getEndInt() + DISPLAY_SIZE);
|
||||
message += " | <a href=\"addressbook.jsp?book=" + getBook() + filterArg +
|
||||
"&begin=" + newBegin + "&end=" + newEnd + "\">" + newBegin +
|
||||
'-' + newEnd + "</a>";
|
||||
@@ -313,7 +326,8 @@ public class AddressbookBean
|
||||
if (deleted == 1)
|
||||
message = _("Destination {0} deleted.", name);
|
||||
else
|
||||
message = _("{0} destinations deleted.", deleted);
|
||||
// parameter will always be >= 2
|
||||
message = ngettext("1 destination deleted.", "{0} destinations deleted.", deleted);
|
||||
}
|
||||
}
|
||||
if( changed ) {
|
||||
@@ -394,29 +408,76 @@ public class AddressbookBean
|
||||
public void setHostname(String hostname) {
|
||||
this.hostname = DataHelper.stripHTML(hostname).trim(); // XSS
|
||||
}
|
||||
|
||||
protected int getBeginInt() {
|
||||
return Math.max(0, Math.min(entries.length - 1, beginIndex));
|
||||
return Math.max(0, Math.min(resultSize() - 1, beginIndex));
|
||||
}
|
||||
|
||||
public String getBegin() {
|
||||
return "" + getBeginInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return beginning index into results
|
||||
* @since 0.8.6
|
||||
*/
|
||||
public String getResultBegin() {
|
||||
return isPrefiltered() ? "0" : Integer.toString(getBeginInt());
|
||||
}
|
||||
|
||||
public void setBegin(String s) {
|
||||
try {
|
||||
beginIndex = Integer.parseInt(s);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
|
||||
protected int getEndInt() {
|
||||
return Math.max(0, Math.max(getBeginInt(), Math.min(entries.length - 1, endIndex)));
|
||||
return Math.max(0, Math.max(getBeginInt(), Math.min(resultSize() - 1, endIndex)));
|
||||
}
|
||||
|
||||
public String getEnd() {
|
||||
return "" + getEndInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ending index into results
|
||||
* @since 0.8.6
|
||||
*/
|
||||
public String getResultEnd() {
|
||||
return Integer.toString(isPrefiltered() ? resultSize() - 1 : getEndInt());
|
||||
}
|
||||
|
||||
public void setEnd(String s) {
|
||||
try {
|
||||
endIndex = Integer.parseInt(s);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the entries map contain only the lookup result,
|
||||
* or must we index into it?
|
||||
* @since 0.8.6
|
||||
*/
|
||||
protected boolean isPrefiltered() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the size of the lookup result
|
||||
* @since 0.8.6
|
||||
*/
|
||||
protected int resultSize() {
|
||||
return entries.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the total size of the address book
|
||||
* @since 0.8.6
|
||||
*/
|
||||
protected int totalSize() {
|
||||
return entries.length;
|
||||
}
|
||||
|
||||
/** translate */
|
||||
protected static String _(String s) {
|
||||
return Messages.getString(s);
|
||||
@@ -433,7 +494,7 @@ public class AddressbookBean
|
||||
}
|
||||
|
||||
/** translate (ngettext) @since 0.8.6 */
|
||||
protected static String _(int n, String s, String p) {
|
||||
protected static String ngettext(String s, String p, int n) {
|
||||
return Messages.getString(n, s, p);
|
||||
}
|
||||
}
|
||||
|
@@ -37,24 +37,58 @@ import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
|
||||
/**
|
||||
* Talk to the NamingService API instead of modifying the hosts.txt files directly
|
||||
* Talk to the NamingService API instead of modifying the hosts.txt files directly,
|
||||
* except for the 'published' addressbook.
|
||||
*
|
||||
* @since 0.8.5
|
||||
* @since 0.8.6
|
||||
*/
|
||||
public class NamingServiceBean extends AddressbookBean
|
||||
{
|
||||
private static final String DEFAULT_NS = "BlockfileNamingService";
|
||||
|
||||
private boolean isDirect() {
|
||||
return getBook().equals("published");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isPrefiltered() {
|
||||
if (isDirect())
|
||||
return super.isPrefiltered();
|
||||
return (search == null || search.length() <= 0) &&
|
||||
(filter == null || filter.length() <= 0);
|
||||
// and right naming service...
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int resultSize() {
|
||||
if (isDirect())
|
||||
return super.resultSize();
|
||||
return isPrefiltered() ? totalSize() : entries.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int totalSize() {
|
||||
if (isDirect())
|
||||
return super.totalSize();
|
||||
// only blockfile needs the list property
|
||||
Properties props = new Properties();
|
||||
props.setProperty("list", getFileName());
|
||||
return getNamingService().size(props);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNotEmpty()
|
||||
{
|
||||
return getNamingService().size() > 0;
|
||||
if (isDirect())
|
||||
return super.isNotEmpty();
|
||||
return totalSize() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName()
|
||||
{
|
||||
if (isDirect())
|
||||
return super.getFileName();
|
||||
loadConfig();
|
||||
String filename = properties.getProperty( getBook() + "_addressbook" );
|
||||
int slash = filename.lastIndexOf('/');
|
||||
@@ -64,7 +98,7 @@ public class NamingServiceBean extends AddressbookBean
|
||||
}
|
||||
|
||||
/** depth-first search */
|
||||
private NamingService searchNamingService(NamingService ns, String srch)
|
||||
private static NamingService searchNamingService(NamingService ns, String srch)
|
||||
{
|
||||
String name = ns.getName();
|
||||
if (name == srch || name == DEFAULT_NS)
|
||||
@@ -88,10 +122,16 @@ public class NamingServiceBean extends AddressbookBean
|
||||
return rv != null ? rv : root;
|
||||
}
|
||||
|
||||
/** Load addressbook and apply filter, returning messages about this. */
|
||||
/**
|
||||
* Load addressbook and apply filter, returning messages about this.
|
||||
* To control memory, don't load the whole addressbook if we can help it...
|
||||
* only load what is searched for.
|
||||
*/
|
||||
@Override
|
||||
public String getLoadBookMessages()
|
||||
{
|
||||
if (isDirect())
|
||||
return super.getLoadBookMessages();
|
||||
NamingService service = getNamingService();
|
||||
Debug.debug("Searching within " + service + " with filename=" + getFileName() + " and with filter=" + filter + " and with search=" + search);
|
||||
String message = "";
|
||||
@@ -100,16 +140,22 @@ public class NamingServiceBean extends AddressbookBean
|
||||
Map<String, Destination> results;
|
||||
Properties searchProps = new Properties();
|
||||
// only blockfile needs this
|
||||
searchProps.setProperty("list", getFileName());
|
||||
searchProps.setProperty("list", getFileName());
|
||||
if (filter != null) {
|
||||
String startsAt = filter == "0-9" ? "0" : filter;
|
||||
String startsAt = filter.equals("0-9") ? "[0-9]" : filter;
|
||||
searchProps.setProperty("startsWith", startsAt);
|
||||
}
|
||||
if (beginIndex > 0)
|
||||
searchProps.setProperty("skip", Integer.toString(beginIndex));
|
||||
int limit = 1 + endIndex - beginIndex;
|
||||
if (limit > 0)
|
||||
searchProps.setProperty("limit", Integer.toString(limit));
|
||||
if (isPrefiltered()) {
|
||||
// Only limit if we not searching or filtering, so we will
|
||||
// know the total number of results
|
||||
if (beginIndex > 0)
|
||||
searchProps.setProperty("skip", Integer.toString(beginIndex));
|
||||
int limit = 1 + endIndex - beginIndex;
|
||||
if (limit > 0)
|
||||
searchProps.setProperty("limit", Integer.toString(limit));
|
||||
}
|
||||
if (search != null && search.length() > 0)
|
||||
searchProps.setProperty("search", search.toLowerCase());
|
||||
results = service.getEntries(searchProps);
|
||||
|
||||
Debug.debug("Result count: " + results.size());
|
||||
@@ -151,6 +197,8 @@ public class NamingServiceBean extends AddressbookBean
|
||||
@Override
|
||||
public String getMessages()
|
||||
{
|
||||
if (isDirect())
|
||||
return super.getMessages();
|
||||
// Loading config and addressbook moved into getLoadBookMessages()
|
||||
String message = "";
|
||||
|
||||
@@ -168,23 +216,22 @@ public class NamingServiceBean extends AddressbookBean
|
||||
} else if (oldDest != null && !action.equals(_("Replace"))) {
|
||||
message = _("Host name {0} is already in addressbook with a different destination. Click \"Replace\" to overwrite.", hostname);
|
||||
} else {
|
||||
boolean valid = true;
|
||||
try {
|
||||
Destination dest = new Destination(destination);
|
||||
getNamingService().put(hostname, dest, nsOptions);
|
||||
boolean success = getNamingService().put(hostname, dest, nsOptions);
|
||||
if (success) {
|
||||
changed = true;
|
||||
if (oldDest == null)
|
||||
message = _("Destination added for {0}.", hostname);
|
||||
else
|
||||
message = _("Destination changed for {0}.", hostname);
|
||||
// clear form
|
||||
hostname = null;
|
||||
destination = null;
|
||||
} else {
|
||||
message = _("Failed to add Destination for {0} to naming service {1}", hostname, getNamingService()) + "<br>";
|
||||
}
|
||||
} catch (DataFormatException dfe) {
|
||||
valid = false;
|
||||
}
|
||||
if (valid) {
|
||||
changed = true;
|
||||
if (oldDest == null)
|
||||
message = _("Destination added for {0}.", hostname);
|
||||
else
|
||||
message = _("Destination changed for {0}.", hostname);
|
||||
// clear form
|
||||
hostname = null;
|
||||
destination = null;
|
||||
} else {
|
||||
message = _("Invalid Base 64 destination.");
|
||||
}
|
||||
}
|
||||
@@ -197,17 +244,20 @@ public class NamingServiceBean extends AddressbookBean
|
||||
String name = null;
|
||||
int deleted = 0;
|
||||
for (String n : deletionMarks) {
|
||||
getNamingService().remove(n, nsOptions);
|
||||
if (deleted++ == 0) {
|
||||
boolean success = getNamingService().remove(n, nsOptions);
|
||||
if (!success) {
|
||||
message += _("Failed to delete Destination for {0} from naming service {1}", name, getNamingService()) + "<br>";
|
||||
} else if (deleted++ == 0) {
|
||||
changed = true;
|
||||
name = n;
|
||||
}
|
||||
}
|
||||
if( changed ) {
|
||||
if (deleted == 1)
|
||||
message = _("Destination {0} deleted.", name);
|
||||
message += _("Destination {0} deleted.", name);
|
||||
else
|
||||
message = _("{0} destinations deleted.", deleted);
|
||||
// parameter will always be >= 2
|
||||
message = ngettext("1 destination deleted.", "{0} destinations deleted.", deleted);
|
||||
}
|
||||
}
|
||||
if( changed ) {
|
||||
|
@@ -144,7 +144,7 @@ ${book.loadBookMessages}
|
||||
<th><%=intl._("Destination")%></th>
|
||||
</tr>
|
||||
<!-- limit iterator, or "Form too large" may result on submit, and is a huge web page if we don't -->
|
||||
<c:forEach items="${book.entries}" var="addr" begin="${book.begin}" end="${book.end}">
|
||||
<c:forEach items="${book.entries}" var="addr" begin="${book.resultBegin}" end="${book.resultEnd}">
|
||||
<tr class="list${book.trClass}">
|
||||
<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>
|
||||
|
Reference in New Issue
Block a user