diff --git a/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java b/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java index 0e9a314a0..f1ba58cd0 100644 --- a/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java +++ b/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java @@ -115,6 +115,7 @@ public class Daemon { // If it is a text file, we do things differently, to avoid O(n**2) behavior // when scanning large subscription results (i.e. those that return the whole file, not just the new entries) - // we load all the known hostnames into a Set one time. + // This also has the advantage of not flushing the NamingService's LRU cache. String nsClass = router.getClass().getSimpleName(); boolean isTextFile = nsClass.equals("HostsTxtNamingService") || nsClass.equals("SingleFileNamingService"); Set knownNames = null; @@ -152,7 +153,9 @@ public class Daemon { if (!isKnown) { if (AddressBook.isValidKey(key)) { Destination dest = new Destination(entry.getValue()); - boolean success = router.put(key, dest); + Properties props = new Properties(); + props.setProperty("s", sub.getLocation()); + boolean success = router.put(key, dest, props); if (log != null) { if (success) log.append("New address " + key + diff --git a/apps/susidns/src/java/src/i2p/susi/dns/AddressBean.java b/apps/susidns/src/java/src/i2p/susi/dns/AddressBean.java index 8e611a112..6fffe6dde 100644 --- a/apps/susidns/src/java/src/i2p/susi/dns/AddressBean.java +++ b/apps/susidns/src/java/src/i2p/susi/dns/AddressBean.java @@ -24,19 +24,19 @@ package i2p.susi.dns; +import java.util.Date; +import java.util.Properties; + import net.i2p.I2PAppContext; import net.i2p.data.Base32; import net.i2p.data.Base64; +import net.i2p.data.Certificate; public class AddressBean { - private String name, destination; + private final String name, destination; + private Properties props; - public AddressBean() - { - - } - public AddressBean(String name, String destination) { this.name = name; @@ -48,21 +48,11 @@ public class AddressBean return destination; } - public void setDestination(String destination) - { - this.destination = destination; - } - public String getName() { return name; } - public void setName(String name) - { - this.name = name; - } - /** @since 0.8.6 */ public String getB32() { @@ -72,4 +62,88 @@ public class AddressBean byte[] hash = I2PAppContext.getGlobalContext().sha().calculateHash(dest).getData(); return Base32.encode(hash) + ".b32.i2p"; } + + /** @since 0.8.6 */ + public void setProperties(Properties p) { + props = p; + } + + /** @since 0.8.6 */ + public String getSource() { + String rv = getProp("s"); + if (rv.startsWith("http://")) + rv = "" + rv + ""; + return rv; + } + + /** @since 0.8.6 */ + public String getAdded() { + return getDate("a"); + } + + /** @since 0.8.6 */ + public String getModded() { + return getDate("m"); + } + + + /** @since 0.8.6 */ + public String getNotes() { + return getProp("notes"); + } + + /** + * Do this the easy way + * @since 0.8.6 + */ + public String getCert() { + // (4 / 3) * (pubkey length + signing key length) + String cert = destination.substring(512); + if (cert.equals("AAAA")) + return _("None"); + byte[] enc = Base64.decode(cert); + if (enc == null) + // shouldn't happen + return "invalid"; + int type = enc[0] & 0xff; + switch (type) { + case Certificate.CERTIFICATE_TYPE_HASHCASH: + return _("Hashcash"); + case Certificate.CERTIFICATE_TYPE_HIDDEN: + return _("Hidden"); + case Certificate.CERTIFICATE_TYPE_SIGNED: + return _("Signed"); + default: + return _("Type {0}", type); + } + } + + /** @since 0.8.6 */ + private String getProp(String p) { + if (props == null) + return ""; + String rv = props.getProperty(p); + return rv != null ? rv : ""; + } + + /** @since 0.8.6 */ + private String getDate(String key) { + String d = getProp(key); + if (d.length() > 0) { + try { + d = FormatDate.format(Long.parseLong(d)); + } catch (NumberFormatException nfe) {} + } + return d; + } + + /** translate */ + private static String _(String s) { + return Messages.getString(s); + } + + /** translate */ + private static String _(String s, Object o) { + return Messages.getString(s, o); + } } diff --git a/apps/susidns/src/java/src/i2p/susi/dns/FormatDate.java b/apps/susidns/src/java/src/i2p/susi/dns/FormatDate.java new file mode 100644 index 000000000..a5fc53218 --- /dev/null +++ b/apps/susidns/src/java/src/i2p/susi/dns/FormatDate.java @@ -0,0 +1,30 @@ +package i2p.susi.dns; + +import java.util.Date; +import java.text.DateFormat; +import java.util.TimeZone; + +import net.i2p.I2PAppContext; + +/** + * Format a date in local time zone + * @since 0.8.6 + */ +public abstract class FormatDate +{ + private static final DateFormat _dateFormat; + + static { + DateFormat fmt = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); + // the router sets the JVM time zone to UTC but saves the original here so we can get it + String systemTimeZone = I2PAppContext.getGlobalContext().getProperty("i2p.systemTimeZone"); + if (systemTimeZone != null) + fmt.setTimeZone(TimeZone.getTimeZone(systemTimeZone)); + _dateFormat = fmt; + } + + public static String format(long date) + { + return _dateFormat.format(new Date(date)); + } +} diff --git a/apps/susidns/src/java/src/i2p/susi/dns/NamingServiceBean.java b/apps/susidns/src/java/src/i2p/susi/dns/NamingServiceBean.java index 1d5d2a12b..b5a7c48e1 100644 --- a/apps/susidns/src/java/src/i2p/susi/dns/NamingServiceBean.java +++ b/apps/susidns/src/java/src/i2p/susi/dns/NamingServiceBean.java @@ -45,6 +45,7 @@ import net.i2p.data.Destination; public class NamingServiceBean extends AddressbookBean { private static final String DEFAULT_NS = "BlockfileNamingService"; + private String detail; private boolean isDirect() { return getBook().equals("published"); @@ -209,7 +210,7 @@ public class NamingServiceBean extends AddressbookBean if( action != null ) { Properties nsOptions = new Properties(); // only blockfile needs this - nsOptions.setProperty("list", getFileName()); + nsOptions.setProperty("list", getFileName()); if( lastSerial != null && serial != null && serial.compareTo( lastSerial ) == 0 ) { boolean changed = false; if (action.equals(_("Add")) || action.equals(_("Replace"))) { @@ -280,4 +281,22 @@ public class NamingServiceBean extends AddressbookBean message = "

" + message + "

"; return message; } + + public void setH(String h) { + this.detail = h; + } + + public AddressBean getLookup() { + if (this.detail == null) + return null; + Properties nsOptions = new Properties(); + Properties outProps = new Properties(); + nsOptions.setProperty("list", getFileName()); + Destination dest = getNamingService().lookup(this.detail, nsOptions, outProps); + if (dest == null) + return null; + AddressBean rv = new AddressBean(this.detail, dest.toBase64()); + rv.setProperties(outProps); + return rv; + } } diff --git a/apps/susidns/src/jsp/addressbook.jsp b/apps/susidns/src/jsp/addressbook.jsp index 9c34fe741..1292e9009 100644 --- a/apps/susidns/src/jsp/addressbook.jsp +++ b/apps/susidns/src/jsp/addressbook.jsp @@ -153,7 +153,7 @@ ${book.loadBookMessages} ${addr.name} (b32) -(<%=intl._("helper")%>) +(<%=intl._("details")%>) diff --git a/apps/susidns/src/jsp/details.jsp b/apps/susidns/src/jsp/details.jsp new file mode 100644 index 000000000..fc03a75cb --- /dev/null +++ b/apps/susidns/src/jsp/details.jsp @@ -0,0 +1,135 @@ +<% +/* + * Created on Sep 02, 2005 + * + * 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 + * + * $Revision: 1.3 $ + */ + + // 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"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> + + + + + + + + + +${book.book} <%=intl._("addressbook")%> - susidns + + + +
+ +
+ +
+
+

<%=intl._(book.getBook())%> <%=intl._("addressbook")%>: ${book.fileName}

+
+ +
+<% + String detail = request.getParameter("h"); + if (detail == null) { + %>

No host specified

<% + } else { + i2p.susi.dns.AddressBean addr = book.getLookup(); + if (addr == null) { + %>

Not found: <%=detail%>

<% + } else { + String b32 = addr.getB32(); +%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
<%=intl._("Host Name")%><%=addr.getName()%>
<%=intl._("Base 32 Address")%><%=b32%>
<%=intl._("Address Helper")%><%=intl._("link")%>
<%=intl._("Public Key")%><%=intl._("ElGamal 2048 bit")%>
<%=intl._("Signing Key")%><%=intl._("DSA 1024 bit")%>
<%=intl._("Certificate")%><%=addr.getCert()%>
<%=intl._("Added Date")%><%=addr.getAdded()%>
<%=intl._("Source")%><%=addr.getSource()%>
<%=intl._("Last Modified")%><%=addr.getModded()%>
<%=intl._("Notes")%><%=addr.getNotes()%>
<%=intl._("Destination")%>
+
+
+
+ + +" > +
+
+<% + } + } +%> +
+ +
+ + diff --git a/core/java/src/net/i2p/client/naming/BlockfileNamingService.java b/core/java/src/net/i2p/client/naming/BlockfileNamingService.java index ee698b96e..448f10f2e 100644 --- a/core/java/src/net/i2p/client/naming/BlockfileNamingService.java +++ b/core/java/src/net/i2p/client/naming/BlockfileNamingService.java @@ -347,17 +347,31 @@ public class BlockfileNamingService extends DummyNamingService { ////////// Start NamingService API + /* + * @param options If non-null and contains the key "list", lookup in + * that list only, otherwise all lists + */ @Override public Destination lookup(String hostname, Properties lookupOptions, Properties storedOptions) { - Destination d = super.lookup(hostname, null, null); - if (d != null) - return d; + String listname = null; + if (lookupOptions != null) + listname = lookupOptions.getProperty("list"); + + Destination d = null; + // only use cache if we aren't retreiving options or specifying the list + if (listname == null && storedOptions == null) { + d = super.lookup(hostname, null, null); + if (d != null) + return d; + } String key = hostname.toLowerCase(); synchronized(_bf) { if (_isClosed) return null; for (String list : _lists) { + if (listname != null && !list.equals(listname)) + continue; try { DestEntry de = getEntry(list, key); if (de != null) {