diff --git a/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java b/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java index 38283aab56e4d4b6b5851ac6b099d32ae0e8f8b9..46660fb4e919312a159de8a262fe28a247b88187 100644 --- a/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java +++ b/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java @@ -574,40 +574,32 @@ public class Daemon { } } // reverse lookup, delete all - // There's no NamingService API to get a list of all reverse - String rev; - String rev2 = null; - while ((rev = router.reverseLookup(pod)) != null) { - // prevent getting stuck from buggy NS - if (rev.equals(rev2)) - break; - rev2 = rev; - // forward check in case hash collision or something - List<Destination> fwd = router.lookupAll(rev); - if (!fwd.contains(pod)) - break; // can't go around again, fail - if (knownNames != null) - knownNames.remove(rev); - boolean success = router.remove(rev, pod); - if (success) - deleted++; - if (log != null) { + List<String> revs = router.reverseLookupAll(pod); + if (revs != null) { + for (String rev : revs) { + if (knownNames != null) + knownNames.remove(rev); + boolean success = router.remove(rev, pod); if (success) - log.append("Removed: " + rev + - " as requested" + - ". From: " + addressbook.getLocation()); - else - log.append("Remove failed for: " + rev + - " as requested" + - ". From: " + addressbook.getLocation()); - } - // now update the published addressbook - if (published != null) { - if (publishedNS == null) - publishedNS = new SingleFileNamingService(I2PAppContext.getGlobalContext(), published.getAbsolutePath()); - success = publishedNS.remove(rev, pod); - if (log != null && !success) - log.append("Remove from published address book " + published.getAbsolutePath() + " failed for " + rev); + deleted++; + if (log != null) { + if (success) + log.append("Removed: " + rev + + " as requested" + + ". From: " + addressbook.getLocation()); + else + log.append("Remove failed for: " + rev + + " as requested" + + ". From: " + addressbook.getLocation()); + } + // now update the published addressbook + if (published != null) { + if (publishedNS == null) + publishedNS = new SingleFileNamingService(I2PAppContext.getGlobalContext(), published.getAbsolutePath()); + success = publishedNS.remove(rev, pod); + if (log != null && !success) + log.append("Remove from published address book " + published.getAbsolutePath() + " failed for " + rev); + } } } } else { diff --git a/apps/i2ptunnel/jsp/register.jsp b/apps/i2ptunnel/jsp/register.jsp index e16e3ad346a67ff57b065bbc30ce011074e54914..0dfb2e10742ce6b967231479779f5e641653e2a6 100644 --- a/apps/i2ptunnel/jsp/register.jsp +++ b/apps/i2ptunnel/jsp/register.jsp @@ -128,7 +128,7 @@ input.default { width: 1px; height: 1px; visibility: hidden; } <label for="signature"> <%=intl._t("Authentication for adding host")%> </label> - <textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Copy and paste this to the registration site" wrap="off" spellcheck="false"><% he.writeProps(out); %></textarea> + <textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Copy and paste this to the registration site" wrap="off" spellcheck="false"><% he.write(out); %></textarea> </div> </div> <div id="tunnelAdvancedNetworking" class="panel"> @@ -180,7 +180,7 @@ input.default { width: 1px; height: 1px; visibility: hidden; } props.setProperty(HostTxtEntry.PROP_ACTION, HostTxtEntry.ACTION_CHANGENAME); props.setProperty(HostTxtEntry.PROP_OLDNAME, oldname); he.sign(spk); - %><textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Copy and paste this to the registration site" wrap="off" spellcheck="false"><% he.writeProps(out); %></textarea> + %><textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Copy and paste this to the registration site" wrap="off" spellcheck="false"><% he.write(out); %></textarea> <span class="comment"><%=intl._t("This will change the name from {0} to {1}, using the same destination", oldname, name)%></span> <% } else { @@ -203,7 +203,7 @@ input.default { width: 1px; height: 1px; visibility: hidden; } props.setProperty(HostTxtEntry.PROP_ACTION, HostTxtEntry.ACTION_ADDNAME); props.setProperty(HostTxtEntry.PROP_OLDNAME, oldname); he.sign(spk); - %><textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Copy and paste this to the registration site" wrap="off" spellcheck="false"><% he.writeProps(out); %></textarea> + %><textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Copy and paste this to the registration site" wrap="off" spellcheck="false"><% he.write(out); %></textarea> <span class="comment"><%=intl._t("This will add an alias {0} for {1}, using the same destination", name, oldname)%></span> <% } else { @@ -227,7 +227,7 @@ input.default { width: 1px; height: 1px; visibility: hidden; } props.setProperty(HostTxtEntry.PROP_OLDDEST, olddest); he.signInner(spk2); he.sign(spk); - %><textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Copy and paste this to the registration site" wrap="off" spellcheck="false"><% he.writeProps(out); %></textarea> + %><textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Copy and paste this to the registration site" wrap="off" spellcheck="false"><% he.write(out); %></textarea> <span class="comment"><%=intl._t("This will change the destination for {0}", name)%></span> <% } else { @@ -252,7 +252,7 @@ input.default { width: 1px; height: 1px; visibility: hidden; } props.setProperty(HostTxtEntry.PROP_OLDDEST, olddest); he.signInner(spk2); he.sign(spk); - %><textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Copy and paste this to the registration site" wrap="off" spellcheck="false"><% he.writeProps(out); %></textarea> + %><textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Copy and paste this to the registration site" wrap="off" spellcheck="false"><% he.write(out); %></textarea> <span class="comment"><%=intl._t("This will add an alternate destination for {0}", name)%></span> <% } else { @@ -278,7 +278,7 @@ input.default { width: 1px; height: 1px; visibility: hidden; } props.setProperty(HostTxtEntry.PROP_OLDDEST, olddest); he.signInner(spk2); he.sign(spk); - %><textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Copy and paste this to the registration site" wrap="off" spellcheck="false"><% he.writeProps(out); %></textarea> + %><textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Copy and paste this to the registration site" wrap="off" spellcheck="false"><% he.write(out); %></textarea> <span class="comment"><%=intl._t("This will add a subdomain {0} of {1}, with a different destination", name, oldname)%></span> <% } else { diff --git a/core/java/src/net/i2p/client/naming/BlockfileNamingService.java b/core/java/src/net/i2p/client/naming/BlockfileNamingService.java index 5dd389adac9871bea63be4300bdd9d4bfd006f97..d51fec18b05486cd2aa038ce825c492343463f5c 100644 --- a/core/java/src/net/i2p/client/naming/BlockfileNamingService.java +++ b/core/java/src/net/i2p/client/naming/BlockfileNamingService.java @@ -620,10 +620,10 @@ public class BlockfileNamingService extends DummyNamingService { * Returns null without exception on error (logs only). * Returns without logging if no reverse skiplist (version 1). * - * @return the first one found if more than one - * @since 0.8.9 + * @return all found if more than one + * @since 0.9.26 from getReverseEntry() 0.8.9 */ - private String getReverseEntry(Hash hash) { + private List<String> getReverseEntries(Hash hash) { try { SkipList<Integer, Properties> rev = _bf.getIndex(REVERSE_SKIPLIST, _hashIndexSerializer, _infoSerializer); if (rev == null) @@ -633,13 +633,21 @@ public class BlockfileNamingService extends DummyNamingService { Properties props = rev.get(idx); if (props == null) return null; - for (Object okey : props.keySet()) { - String key = (String) okey; + List<String> rv = new ArrayList<String>(props.size()); + for (String key : props.stringPropertyNames()) { // now do the forward lookup to verify (using the cache) - Destination d = lookup(key); - if (d != null && d.calculateHash().equals(hash)) - return key; + List<Destination> ld = lookupAll(key); + if (ld != null) { + for (Destination d : ld) { + if (d.calculateHash().equals(hash)) { + rv.add(key); + break; + } + } + } } + if (!rv.isEmpty()) + return rv; } catch (IOException ioe) { _log.error("DB get reverse error", ioe); } catch (RuntimeException e) { @@ -1385,10 +1393,33 @@ public class BlockfileNamingService extends DummyNamingService { */ @Override public String reverseLookup(Hash h) { + List<String> ls; + synchronized(_bf) { + if (_isClosed) + return null; + ls = getReverseEntries(h); + } + return (ls != null) ? ls.get(0) : null; + } + + /** + * @param options ignored + * @since 0.9.26 + */ + @Override + public List<String> reverseLookupAll(Destination d, Properties options) { + return reverseLookupAll(d.calculateHash()); + } + + /** + * @since 0.9.26 + */ + @Override + public List<String> reverseLookupAll(Hash h) { synchronized(_bf) { if (_isClosed) return null; - return getReverseEntry(h); + return getReverseEntries(h); } } diff --git a/core/java/src/net/i2p/client/naming/HostTxtEntry.java b/core/java/src/net/i2p/client/naming/HostTxtEntry.java index d68a4c80c16e94d57fedb226e8ba35ad8ad3e4c7..8d6f967e01f955d5409a925eabf85032e2c15055 100644 --- a/core/java/src/net/i2p/client/naming/HostTxtEntry.java +++ b/core/java/src/net/i2p/client/naming/HostTxtEntry.java @@ -134,13 +134,21 @@ public class HostTxtEntry { * Includes newline. */ public void write(BufferedWriter out) throws IOException { + write((Writer) out); + out.newLine(); + } + + /** + * Write as a standard line name=dest[#!k1=v1#k2=v2...] + * Does not include newline. + */ + public void write(Writer out) throws IOException { if (name != null && dest != null) { out.write(name); out.write(KV_SEPARATOR); out.write(dest); } writeProps(out); - out.newLine(); } /** diff --git a/core/java/src/net/i2p/client/naming/NamingService.java b/core/java/src/net/i2p/client/naming/NamingService.java index 8d3d9e03ff9ea60ff44cfc6a72da099915a6f1ef..d71702d88b25c17330dd4ed1ab62a5b3b02790b2 100644 --- a/core/java/src/net/i2p/client/naming/NamingService.java +++ b/core/java/src/net/i2p/client/naming/NamingService.java @@ -691,6 +691,51 @@ public abstract class NamingService { return remove(hostname, options); } + /** + * Reverse lookup a hash. + * This implementation returns the result from reverseLookup, or null. + * Subclasses implementing reverse lookups should override. + * + * @param h non-null + * @return a non-empty list of host names for this hash, or <code>null</code> + * if none is known. It is safe for subclasses to always return + * <code>null</code> if no reverse lookup is possible. + * @since 0.9.26 + */ + public List<String> reverseLookupAll(Hash h) { + String s = reverseLookup(h); + return (s != null) ? Collections.singletonList(s) : null; + } + + /** + * Reverse lookup a destination + * This implementation returns reverseLookupAll(dest, null). + * + * @param dest non-null + * @return a non-empty list of host names for this Destination, or <code>null</code> + * if none is known. It is safe for subclasses to always return + * <code>null</code> if no reverse lookup is possible. + * @since 0.9.26 + */ + public List<String> reverseLookupAll(Destination dest) { + return reverseLookupAll(dest, null); + } + + /** + * Same as reverseLookupAll(dest) but with options + * This implementation returns the result from reverseLookup, or null. + * Subclasses implementing reverse lookups should override. + * + * @param d non-null + * @param options NamingService-specific, can be null + * @return a non-empty list of host names for this Destination, or <code>null</code> + * @since 0.9.26 + */ + public List<String> reverseLookupAll(Destination d, Properties options) { + String s = reverseLookup(d, options); + return (s != null) ? Collections.singletonList(s) : null; + } + //// End new API for multiple Destinations /**