diff --git a/apps/susidns/src/java/src/i2p/susi/dns/AddressByNameSorter.java b/apps/susidns/src/java/src/i2p/susi/dns/AddressByNameSorter.java index 1c503a999..6421f23a9 100644 --- a/apps/susidns/src/java/src/i2p/susi/dns/AddressByNameSorter.java +++ b/apps/susidns/src/java/src/i2p/susi/dns/AddressByNameSorter.java @@ -26,13 +26,10 @@ package i2p.susi.dns; import java.util.Comparator; -public class AddressByNameSorter implements Comparator +public class AddressByNameSorter implements Comparator { - public int compare(Object arg0, Object arg1) + public int compare(AddressBean a, AddressBean b) { - AddressBean a = (AddressBean)arg0; - AddressBean b = (AddressBean)arg1; - if( a == null ) return 1; diff --git a/apps/susidns/src/java/src/i2p/susi/dns/AddressbookBean.java b/apps/susidns/src/java/src/i2p/susi/dns/AddressbookBean.java index b6af8a12d..b3239a2d7 100644 --- a/apps/susidns/src/java/src/i2p/susi/dns/AddressbookBean.java +++ b/apps/susidns/src/java/src/i2p/susi/dns/AddressbookBean.java @@ -42,12 +42,13 @@ import net.i2p.util.SecureFileOutputStream; public class AddressbookBean { - private String book, action, serial, lastSerial, filter, search, hostname, destination; - private int beginIndex, endIndex; - private Properties properties, addressbook; + protected String book, action, serial, lastSerial, filter, search, hostname, destination; + protected int beginIndex, endIndex; + protected final Properties properties; + private Properties addressbook; private int trClass; - private LinkedList deletionMarks; - private static Comparator sorter; + protected final LinkedList deletionMarks; + protected static final Comparator sorter; private static final int DISPLAY_SIZE=100; static { @@ -88,7 +89,7 @@ public class AddressbookBean private long configLastLoaded = 0; private static final String PRIVATE_BOOK = "private_addressbook"; private static final String DEFAULT_PRIVATE_BOOK = "../privatehosts.txt"; - private void loadConfig() + protected void loadConfig() { long currentTime = System.currentTimeMillis(); @@ -123,11 +124,14 @@ public class AddressbookBean } catch (IOException ioe) {} return filename; } - private Object[] entries; - public Object[] getEntries() + + protected AddressBean[] entries; + + public AddressBean[] getEntries() { return entries; } + public String getAction() { return action; } @@ -167,7 +171,7 @@ public class AddressbookBean try { fis = new FileInputStream( getFileName() ); addressbook.load( fis ); - LinkedList list = new LinkedList(); + LinkedList list = new LinkedList(); Enumeration e = addressbook.keys(); while( e.hasMoreElements() ) { String name = (String)e.nextElement(); @@ -189,52 +193,11 @@ public class AddressbookBean } list.addLast( new AddressBean( name, destination ) ); } - Object array[] = list.toArray(); + AddressBean array[] = list.toArray(new AddressBean[list.size()]); Arrays.sort( array, sorter ); entries = array; - // Format a message about filtered addressbook size, and the number of displayed entries - // addressbook.jsp catches the case where the whole book is empty. - String filterArg = ""; - if( search != null && search.length() > 0 ) { - message = _("Search") + ' '; - } - if( filter != null && filter.length() > 0 ) { - if( search != null && search.length() > 0 ) - message = _("Search within filtered list") + ' '; - else - message = _("Filtered list") + ' '; - 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 (entries.length == 1) - message += _("contains 1 entry"); - else - message += _("contains {0} entries", entries.length); - message += '.'; - } else { - if (getBeginInt() > 0) { - int newBegin = Math.max(0, getBeginInt() - DISPLAY_SIZE); - int newEnd = Math.max(0, getBeginInt() - 1); - message += "" + newBegin + - '-' + newEnd + " | "; - } - 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 += " | " + newBegin + - '-' + newEnd + ""; - } - } + message = generateLoadMessage(); } catch (Exception e) { Debug.debug( e.getClass().getName() + ": " + e.getMessage() ); @@ -246,6 +209,54 @@ public class AddressbookBean message = "

" + message + "

"; return message; } + + /** + * Format a message about filtered addressbook size, and the number of displayed entries + * addressbook.jsp catches the case where the whole book is empty. + */ + protected String generateLoadMessage() { + String message = ""; + String filterArg = ""; + if( search != null && search.length() > 0 ) { + message = _("Search") + ' '; + } + if( filter != null && filter.length() > 0 ) { + if( search != null && search.length() > 0 ) + message = _("Search within filtered list") + ' '; + else + message = _("Filtered list") + ' '; + 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 + message += _(entries.length, "contains 1 entry", "contains {0} entries"); + message += '.'; + } else { + if (getBeginInt() > 0) { + int newBegin = Math.max(0, getBeginInt() - DISPLAY_SIZE); + int newEnd = Math.max(0, getBeginInt() - 1); + message += "" + newBegin + + '-' + newEnd + " | "; + } + 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 += " | " + newBegin + + '-' + newEnd + ""; + } + } + return message; + } + /** Perform actions, returning messages about this. */ public String getMessages() { @@ -255,8 +266,6 @@ public class AddressbookBean if( action != null ) { if( lastSerial != null && serial != null && serial.compareTo( lastSerial ) == 0 ) { boolean changed = false; - int deleted = 0; - String name = null; if (action.equals(_("Add")) || action.equals(_("Replace"))) { if( addressbook != null && hostname != null && destination != null ) { String oldDest = (String) addressbook.get(hostname); @@ -291,12 +300,14 @@ public class AddressbookBean // clear search when adding search = null; } else if (action.equals(_("Delete Selected"))) { - Iterator it = deletionMarks.iterator(); - while( it.hasNext() ) { - name = (String)it.next(); - addressbook.remove( name ); - changed = true; - deleted++; + String name = null; + int deleted = 0; + for (String n : deletionMarks) { + addressbook.remove(n); + if (deleted++ == 0) { + changed = true; + name = n; + } } if( changed ) { if (deleted == 1) @@ -337,6 +348,7 @@ public class AddressbookBean fos.close(); } catch (IOException ioe) {} } + public String getFilter() { return filter; } @@ -382,7 +394,7 @@ public class AddressbookBean public void setHostname(String hostname) { this.hostname = DataHelper.stripHTML(hostname).trim(); // XSS } - private int getBeginInt() { + protected int getBeginInt() { return Math.max(0, Math.min(entries.length - 1, beginIndex)); } public String getBegin() { @@ -393,7 +405,7 @@ public class AddressbookBean beginIndex = Integer.parseInt(s); } catch (NumberFormatException nfe) {} } - private int getEndInt() { + protected int getEndInt() { return Math.max(0, Math.max(getBeginInt(), Math.min(entries.length - 1, endIndex))); } public String getEnd() { @@ -406,17 +418,22 @@ public class AddressbookBean } /** translate */ - private static String _(String s) { + protected static String _(String s) { return Messages.getString(s); } /** translate */ - private static String _(String s, Object o) { + protected static String _(String s, Object o) { return Messages.getString(s, o); } /** translate */ - private static String _(String s, Object o, Object o2) { + protected static String _(String s, Object o, Object o2) { return Messages.getString(s, o, o2); } + + /** translate (ngettext) @since 0.8.6 */ + protected static String _(int n, String s, String p) { + return Messages.getString(n, s, p); + } } diff --git a/apps/susidns/src/java/src/i2p/susi/dns/Messages.java b/apps/susidns/src/java/src/i2p/susi/dns/Messages.java index b596a3be9..7d35e80f0 100644 --- a/apps/susidns/src/java/src/i2p/susi/dns/Messages.java +++ b/apps/susidns/src/java/src/i2p/susi/dns/Messages.java @@ -31,4 +31,9 @@ public class Messages { public static String getString(String s, Object o, Object o2) { return Translate.getString(s, o, o2, I2PAppContext.getGlobalContext(), BUNDLE_NAME); } + + /** translate (ngettext) @since 0.8.6 */ + public static String getString(int n, String s, String p) { + return Translate.getString(n, s, p, I2PAppContext.getGlobalContext(), BUNDLE_NAME); + } } diff --git a/apps/susidns/src/java/src/i2p/susi/dns/NamingServiceBean.java b/apps/susidns/src/java/src/i2p/susi/dns/NamingServiceBean.java new file mode 100644 index 000000000..3caf8d602 --- /dev/null +++ b/apps/susidns/src/java/src/i2p/susi/dns/NamingServiceBean.java @@ -0,0 +1,228 @@ +/* + * This file is part of susidns project, see http://susi.i2p/ + * + * Copyright (C) 2005 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @since 0.8.6 + */ + +package i2p.susi.dns; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import net.i2p.I2PAppContext; +import net.i2p.client.naming.NamingService; +import net.i2p.data.DataFormatException; +import net.i2p.data.DataHelper; +import net.i2p.data.Destination; + +/** + * Talk to the NamingService API instead of modifying the hosts.txt files directly + * + * @since 0.8.5 + */ +public class NamingServiceBean extends AddressbookBean +{ + private static final String DEFAULT_NS = "BlockfileNamingService"; + + + @Override + public boolean isNotEmpty() + { + return getNamingService().size() > 0; + } + + @Override + public String getFileName() + { + loadConfig(); + String filename = properties.getProperty( getBook() + "_addressbook" ); + int slash = filename.lastIndexOf('/'); + if (slash >= 0) + filename = filename.substring(slash + 1); + return filename; + } + + /** depth-first search */ + private NamingService searchNamingService(NamingService ns, String srch) + { + String name = ns.getName(); + if (name == srch || name == DEFAULT_NS) + return ns; + List list = ns.getNamingServices(); + if (list != null) { + for (NamingService nss : list) { + NamingService rv = searchNamingService(nss, srch); + if (rv != null) + return rv; + } + } + return null; + } + + /** @return the NamingService for the current file name, or the root NamingService */ + private NamingService getNamingService() + { + NamingService root = I2PAppContext.getGlobalContext().namingService(); + NamingService rv = searchNamingService(root, getFileName()); + return rv != null ? rv : root; + } + + /** Load addressbook and apply filter, returning messages about this. */ + @Override + public String getLoadBookMessages() + { + NamingService service = getNamingService(); + Debug.debug("Searching within " + service + " with filename=" + getFileName() + " and with filter=" + filter + " and with search=" + search); + String message = ""; + try { + LinkedList list = new LinkedList(); + Map results; + Properties searchProps = new Properties(); + // only blockfile needs this + searchProps.setProperty("list", getFileName()); + if (filter != null) { + String startsAt = filter == "0-9" ? "0" : 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)); + results = service.getEntries(searchProps); + + Debug.debug("Result count: " + results.size()); + for (Map.Entry entry : results.entrySet()) { + String name = entry.getKey(); + if( filter != null && filter.length() > 0 ) { + if( filter.compareTo( "0-9" ) == 0 ) { + char first = name.charAt(0); + if( first < '0' || first > '9' ) + continue; + } + else if( ! name.toLowerCase().startsWith( filter.toLowerCase() ) ) { + continue; + } + } + if( search != null && search.length() > 0 ) { + if( name.indexOf( search ) == -1 ) { + continue; + } + } + String destination = entry.getValue().toBase64(); + list.addLast( new AddressBean( name, destination ) ); + } + AddressBean array[] = list.toArray(new AddressBean[list.size()]); + Arrays.sort( array, sorter ); + entries = array; + + message = generateLoadMessage(); + } + catch (Exception e) { + Debug.debug( e.getClass().getName() + ": " + e.getMessage() ); + } + if( message.length() > 0 ) + message = "

" + message + "

"; + return message; + } + + /** Perform actions, returning messages about this. */ + @Override + public String getMessages() + { + // Loading config and addressbook moved into getLoadBookMessages() + String message = ""; + + if( action != null ) { + Properties nsOptions = new Properties(); + // only blockfile needs this + nsOptions.setProperty("list", getFileName()); + if( lastSerial != null && serial != null && serial.compareTo( lastSerial ) == 0 ) { + boolean changed = false; + if (action.equals(_("Add")) || action.equals(_("Replace"))) { + if(hostname != null && destination != null) { + Destination oldDest = getNamingService().lookup(hostname, nsOptions, null); + if (oldDest != null && destination.equals(oldDest.toBase64())) { + message = _("Host name {0} is already in addressbook, unchanged.", hostname); + } 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); + } 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."); + } + } + } else { + message = _("Please enter a host name and destination"); + } + // clear search when adding + search = null; + } else if (action.equals(_("Delete Selected"))) { + String name = null; + int deleted = 0; + for (String n : deletionMarks) { + getNamingService().remove(n, nsOptions); + if (deleted++ == 0) { + changed = true; + name = n; + } + } + if( changed ) { + if (deleted == 1) + message = _("Destination {0} deleted.", name); + else + message = _("{0} destinations deleted.", deleted); + } + } + if( changed ) { + message += "
" + _("Addressbook saved."); + } + } + else { + message = _("Invalid form submission, probably because you used the \"back\" or \"reload\" button on your browser. Please resubmit."); + } + } + + action = null; + + if( message.length() > 0 ) + message = "

" + message + "

"; + return message; + } +} diff --git a/apps/susidns/src/jsp/addressbook.jsp b/apps/susidns/src/jsp/addressbook.jsp index 15c76dfc8..e6729f6b0 100644 --- a/apps/susidns/src/jsp/addressbook.jsp +++ b/apps/susidns/src/jsp/addressbook.jsp @@ -32,7 +32,7 @@ <%@ page contentType="text/html"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> - + @@ -55,10 +55,10 @@